Shrink Your ClojureScript Bundle: Using :advanced Optimizations with Externs
Shrink your ClojureScript bundle by enabling :advanced compiler optimizations and adding proper externs for external libraries like lodash. Follow a concrete example, verify runtime integrity, and understand the trade‑offs.
26 Dec 2025, 03:22 UTC

Why Size Matters in a ClojureScript Project
When you ship a web app, the first thing users notice is how fast it loads. Even a few hundred kilobytes of JavaScript can add seconds to the first paint, especially on mobile devices or slow networks. ClojureScript’s default compiler settings – :simple or :whitespace – preserve readability at the cost of size. If you’re pulling in heavy external libraries, the bundle can balloon to several megabytes.
Thesis: Advanced Mode + Externs = Big Size Savings
The Closure Compiler’s :advanced mode aggressively mangles property names, removes dead code, and inlines functions. When you enable it, you need to tell the compiler which JavaScript symbols are “externs” – that is, symbols that live outside the compiled bundle and must not be renamed or removed. With a correct externs file you can shrink a 1.5 MB bundle to under 900 KB without breaking interop.
Step‑by‑Step: A Minimal Example
1. Project Setup
mkdir clj-external-optim && cd clj-external-optim
;; Create a minimal Shadow CLJS project
clj -M -m shadow.cljs.devtools.cli init
# Add lodash via npm
npm install lodash
We’ll use lodash as the external library because it’s widely used and its API is stable.
2. Write an Externs File
Externs describe the public API of the external library. Create src/main/lodash.externs.js with the following content:
/** @externs */
var _ = {};
// @ts-ignore
_.merge = function(...args) {};
// @ts-ignore
_.cloneDeep = function(value) {};
Each function you plan to call must be declared, even if you don’t provide an implementation. The Closure Compiler will keep these names intact.
3. Configure Shadow CLJS
Edit shadow-cljs.edn to enable advanced mode and point to the externs file. Add a new build target called app-prod:
(:builds
{:app-prod
{:target :browser
:output-dir "resources/public/js"
:asset-path "/js"
:modules {:app {:entries [my-app.core]}}
:devtools {:http-root "resources/public" :http-port 3000}
:optimizations :advanced
:externs ["src/main/lodash.externs.js"]})
Make sure the entries point to your main namespace.
4. Build and Compare
Run the build:
npx shadow-cljs release app-prod
Locate the generated bundle at resources/public/js/app.js. To see the real-world impact, compare the gzipped sizes of the app.js produced with :whitespace and with :advanced. A typical reduction is 30–50 % if the externs are accurate.
5. Verify Runtime Integrity
- Run your unit tests against both builds. All tests should pass.
- Open the browser console and check for errors related to missing symbols.
- Inspect the generated JavaScript: property names like
_.mergeshould remain unchanged.
Trade‑offs and Limitations
- Externs Accuracy: If you forget a property or mis‑spell a name, the compiler will rename it, leading to runtime failures that are hard to trace.
- Debugging Difficulty: Advanced mode mangles names, so stack traces are less readable. Rely on source maps and thorough testing.
- Build Time: Advanced mode takes longer to compile. For large projects, consider incremental builds or turning off advanced mode for development.
Practical Checklist Before You Switch
- Document all external symbols you use.
- Create or update externs files accordingly.
- Run a quick build with
:whitespaceto baseline size. - Build with
:advancedand compare sizes. - Run the full test suite and smoke‑test the app in a staging environment.
- If any failures arise, refine externs and re‑build.
Takeaway
Enabling :advanced optimizations in ClojureScript can dramatically shrink your bundle, but it hinges on a complete and accurate externs file. Treat externs as a contract between your ClojureScript code and the JavaScript ecosystem. With careful preparation and rigorous testing, you can enjoy faster page loads without sacrificing functionality.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.