How to compile/build a ClojureScript project?

4 min readDec 16, 2018

For the impatient: just show the tutorial, how to compile and run(with shadow-cljs)?

There was a poll on Twitter I tried to ask people why they don’t like ClojureScript,

Many people responded with “Not known how to build it”. By the word “build”, I mean mostly “compiling”. Not sure if someone takes it like “structuring” or something.

The “compiling” problem is much easier today I would explain later. For the “structuring” part, I don’t think I can really answer it. To create a browser project you will need a UI library like Reagent, Rum, Om, or even use React(with hooks) directly.(I made a virtual DOM library too if you want to try. To create a Node.js projects, you would need to call Node.js APIs and import npm modules. Not much I can tell.

On compiling

I’m going to talk about the “compiling” part, trying to guide you through the first steps with my experiences. Might be a bit inaccurate, but supposed to be helpful.

The core library of compiling ClojureScript is cljs.main provided by the ClojureScript compiler. You may see it in the official guide, telling you how to compile project, how to turn on optimizations, how to start REPL in Node.js code, etc.

However it’s not quite enough if you want to build a larger project. You may known Webpack already, so you think there’s hot module replacement as you save files, you may need to grab packages from npm and put in your projects. cljs.main does not help with such needs. There are a few projects to help which are listed in ClojureScript beginner page:


shadow-cljs shines because it has user experiences very similar to Webpack. It bundles npm modules(JavaScript part, not CSS or images) into your project seamlessly. Most of the packages can be used in your projects and built with shadow-cljs out of box. Even shadow-cljs itself is installed from npm, just like Webpack does.

Meanwhile shadow-cljs has a configuration file written in EDN format. That means you can turn on hot code swapping and optimizations. It also handles externs for you. If you haven’t used Google Closure Compiler and not familiar with externs, it might be freaky. shadow-cljs can handle that.

You may find more on shadow-cljs Users Guide, or just take a look at how I use it to compile.


There was lein-figwheel before figwheel-main. “Lein” refers to “Leiningen”, which is a popular build tool on JVM Clojure side. Lein has problems like starting-up very slow, so fighwheel-main come as result since we have Clojure CLI now.

Figwheel was created long before shadow-cljs released in 2017. It has many nice features, especially for how code swapping. Many of the features just inspired shadow-cljs.

I’m not that familiar with figwheel-main after switching to shadow-cljs. But you can take a look at Figwheel’s Tutorial to know how it works.


It’s like a very early build tool which wraps APIs from ClojureScript compiler. I was using it when I was first learning ClojureScript. To be honest, I don’t like it. But writing Lein configurations is an easier way in the old days.

Boot-cljs and Boot-reload

Boot another build tool, like Lein, but composable, which makes it a little more powerful than Lein and people can write code to define how the workflow is like. The features are similar, just more flexible. boot-reload is the part to perform hot code swapping paired with boot-cljs.

The bad part is boot-cljs, as well as lein-cljsbuild, are simpler wrappers on ClojureScript APIs. When you want to add long term caching like Webpack, you have to implement your own. shadow-cljs actually can generate MD5 hash appending to the filename, so I switched.

Lumo and Planck

They are REPLs, built with self-hosted ClojureScript. The tools above runs on JVM, which might be unfamiliar to JavaScript developers. And Planck runs on JavaScriptCore, Lumo was created later and runs on V8. By running on V8, Lumo supports running all npm modules, which is a great step towards JavaScript community.

They also includes APIs for compiling ClojureScript into plain JavaScript. However I hold the opinion that they are really good REPLs, but I would prefer not developing my projects with them, because hot code swapping on file saving are really useful, there are not supported in these REPL, along with some other interesting features.

Other things to know

ClojureScript has a strange part which is Google Closure Compiler. Webpack people does not prefer using Google Closure Compiler, while it’s commonly used in ClojureScript. It’s good at dead code elimination, which ClojureScript really rely on. However Google Closure Compiler is really unfamiliar to most JavaScript people. It does not work well with Webpack too. Have to accept that. Luckily you don’t need to know much about that. Just keep in mind that it’s something different from Webpack.

Clojure has macros, which mean in compiling ClojureScript, there’s a change to define macros in .clj files and then use it. Then you have to power of running part of the code at compilation. This is quite interesting, because there are two phases of evaluating your code now. JavaScript only runs code in runtime, which is only one phase.

Might be more you would need to know to compile ClojureScript, but I have to stop here. Reply if you got ideas.

If you want a simple suggestion, I would just recommend shadow-cljs. It’s much like Webpack, if you are familiar with Webpack, you will feel shadow-cljs has quite some familiar parts. Otherwise figwheel is also cool.