Sept. 10, 2023

Today I put together a small test repo to check how much space is saved when replacing React with Preact in a ClojureScript project.

I used npm init shadowfront prtest to get a basic project up and running. This creates a simple one page Reagent app with a button you can click.

(ns prtest.core
  (:require
    [reagent.core :as r]
    [reagent.dom :as rdom]))

(defonce state (r/atom {}))

(defn component-main [_state]
  [:div
   [:h1 "prtest"]
   [:p "Welcome to the app!"]
   [:button {:on-click #(js/alert "Hello world!")}
            "click me"]])

(defn start {:dev/after-load true} []
  (rdom/render [component-main state]
               (js/document.getElementById "app")))

(defn init []
  (start))

I made a build to check the size of the resulting js binary. Then I uninstalled react and react-dom and installed preact@8 and preact-compat. Then I updated shadow-cljs.edn to add the following clause into the :app build:

:js-options {:resolve {"react" {:target :npm :require "preact-compat"}
                       "react-dom" {:target :npm :require "preact-compat"}}

This asks shadow-cljs to alias those React modules to the Preact compatibility layer throughout the whole stack.

I ran make to build the project before and after the change and got the following results:

  1. With React = 292k
    $ du -hs build/js/main.js 
    292K    build/js/main.js
  1. With Preact = 172k
    $ du -hs build/js/main.js 
    172K    build/js/main.js

A 41% size reduction (120k) for a simple one page app seems pretty good. Most of the remaining 172k would be ClojureScript core and libraries such as Reagent.

Update: shadow-cljs lets us generate a build report. Here's a build report with React and then Preact:

React ClojureScript build report

Package Weight %
react-dom @ npm: 18.2.0 128.22 KB 45.8 %
org.clojure/clojurescript @ mvn: 1.11.60 115.71 KB 41.4 %
reagent @ mvn: 1.1.0 22.93 KB 8.2 %
react @ npm: 18.2.0 6.49 KB 2.3 %
scheduler @ npm: 0.23.0 3.96 KB 1.4 %
org.clojure/google-closure-library @ mvn: 0.0-20230227-c7c0a541 1.12 KB 0.4 %
Generated Files 932 0.3 %
src 490 0.2 %

Preact ClojureScript build report

Package Weight %
org.clojure/clojurescript @ mvn: 1.11.60 115.61 KB 71.4 %
reagent @ mvn: 1.1.0 22.94 KB 14.2 %
preact-compat @ npm: 3.19.0 9.24 KB 5.7 %
preact @ npm: 8.5.3 8.18 KB 5.1 %
preact-context @ npm: 1.1.4 2.55 KB 1.6 %
org.clojure/google-closure-library @ mvn: 0.0-20230227-c7c0a541 1.12 KB 0.7 %
Generated Files 931 0.6 %
prop-types @ npm: 15.8.1 801 0.5 %
src 490 0.3 %