shadow-cljs
provides everything you need to compile your ClojureScript code with a focus on simplicity and ease of use.
Features
- Good configuration defaults so you don't have to sweat the details
- Seamless
npm
integration - Fast builds, reliable caching, ...
- Supporting various targets
:browser
,:node-script
,:npm-module
,:react-native
,:chrome-extension
, ... - Live Reload (CLJS + CSS)
- CLJS REPL
- Code splitting (via
:modules
)
Requirements
- Node.js or Bun
- npm (comes with Node.js), bun (comes with Bun), pnpm, or yarn
- Java SDK (Version 11+, Latest LTS Version recommended)
Quick Start
Creating your project can be done quickly using the npx create-cljs-project
utility. npx
is part of npm
and lets us run utility scripts quickly without worrying about installing them first. The installer will create a basic project scaffold and install the latest version of shadow-cljs
in the project.
$ npx create-cljs-project acme-app
npx: installed 1 in 5.887s
shadow-cljs - creating project: .../acme-app
Creating: .../acme-app/package.json
Creating: .../acme-app/shadow-cljs.edn
Creating: .../acme-app/.gitignore
Creating: .../acme-app/src/main
Creating: .../acme-app/src/test
----
Installing shadow-cljs in project.
npm notice created a lockfile as package-lock.json. You should commit this file.
+ shadow-cljs@<version>
added 88 packages from 103 contributors and audited 636 packages in 6.287s
found 0 vulnerabilities
----
Done.
----
The resulting project has the following structure
.
├── node_modules (omitted ...)
├── package.json
├── package-lock.json
├── shadow-cljs.edn
└── src
├── main
└── test
shadow-cljs.edn
will be used to configure your CLJS builds and CLJS dependencies. package.json
is used by npm
to manage JS dependencies.
Everything is ready to go if you just want to start playing with a REPL
$ npx shadow-cljs node-repl
# or
$ npx shadow-cljs browser-repl
When building actual projects we need to configure the build first and create at least one source file.
The default source paths are configured to use src/main
as the primary source directory. It is recommended to follow the Java Naming Conventions to organize your CLJS namespaces. It is recommended to start all namespaces with a unique enough prefix (eg. company name, project name) to avoid conflicts with generic names such as app.core
. Suppose you were building a Web Frontend for Acme Inc. using acme.frontend.app
might be a good starting point as it can easily grow to include acme.backend.*
later on.
Using the above example the expected filename for acme.frontend.app
is src/main/acme/frontend/app.cljs
.
Lets start with a simple example for a browser-based build.
(ns acme.frontend.app)
(defn init []
(println "Hello World"))
Inside the shadow-cljs.edn
:builds
section add
{...
:builds
{:frontend
{:target :browser
:modules {:main {:init-fn acme.frontend.app/init}}
}}}
This config tells the compiler to call (acme.frontend.app/init)
when the generated JS code is loaded. Since no :output-dir
is configured the default public/js
is used. You can start the development process by running:
$ npx shadow-cljs watch frontend
...
a few moments later ...
...
[:frontend] Build completed. (134 files, 35 compiled, 0 warnings, 5.80s)
The compilation will create the public/js/main.js
we configured above (:main
becomes main.js
in the :output-dir
). Since we want to load this in the browser we need to create a HTML file in public/index.html
.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>acme frontend</title>
</head>
<body>
<div id="root"></div>
<script src="/js/main.js"></script>
</body>
</html>
We also need a simple HTTP server to serve our HTML since modern Browsers all place a few restrictions on files loaded directly from disk which will lead to issues later. shadow-cljs
provides such a server but you can use anything you like at this point. It only matters that the files from the public
directory are served properly. To start the built-in web server just adjust the build config from above.
{...
:dev-http {8080 "public"}
:builds
{:frontend
{:target :browser
:modules {:main {:init-fn acme.frontend.app/init}}
}}}
Once the config is saved the server will automatically start and serve the content at http://localhost:8080. There is no need to restart shadow-cljs
. When opening the above URL the Browser Console should show "Hello World".
To be continued ...
Documentation
Please refer to the User Manual. (Work in Progress)
Video Courses
- [EN] Learn Reagent Free - reagent+firebase demo application built using shadow-cljs
- [EN] Learn Reagent Pro [Affiliate Link, 30$ discount] - reagent+firebase demo application built using shadow-cljs
- [EN] Learn re-frame [Affiliate Link] - re-frame SPA tutorial
- [EN] ClojureScript for React Developer - Building Conduit
Guides
- [EN] A beginner guide to compile ClojureScript with shadow-cljs
- [CN] shadow-cljs 2.x 使用教程
- [JA] Shadow CLJS User’s Guide 邦訳
- [EN] ClojureScript with Middleman via Shadow-CLJS
- [EN] Clojurescript Development for JavaScript Developers in Atom with Shadow-cljs and ProtoREPL
- [EN] Confidence and Joy: React Native Development with ClojureScript and re-frame
- ... please let me know if you created something to include here
Examples
- Official Browser Example
- re-frame-template - Leiningen template that creates a re-frame project using the shadow-cljs build tool with many optional extras.
- mhuebert/shadow-re-frame - Usage of re-frame, re-frame-trace, and the shadow-cljs build tool. Live Demo
- jacekschae/shadow-reagent - shadow-cljs + proto-repl + reagent
- jacekschae/shadow-firebase - shadow-cljs + firebase
- ahonn/shadow-electron-starter - ClojureScript + Shadow-cljs + Electron + Reagent
- jacekschae/conduit - Real world application built with shadow-cljs + re-frame + re-frame-10x
Demo | Demo with re-frame-10x - quangv/shadow-re-frame-simple-example - a simple re-frame + shadow-cljs example.
- CryptoTwittos - reagent, re-frame, web3
- loganpowell/shadow-proto-starter - shadow-cljs, Atom, Proto-REPL, node.js library
- manuel-uberti/boodle - re-frame based Accounting SPA with
deps.edn
based backend - shadow-cljs-kitchen-async-puppeteer - Automated browser test with puppeteer and cljs.test, built with shadow-cljs
- baskeboler/cljs-karaoke-client - web karaoke player using shadow-cljs + reagent + re-frame + re-frame-10x + re-frame-async-flow-fx + build hooks for minifying css and generating seo pages (Demo)
- flexsurfer/ClojureRNProject - simple React Native application with ClojureScript, re-frame and react navigation v5
- jacekschae/shadow-cljs-devcards - how to configure devcards with shadow-cljs
- jacekschae/shadow-cljs-tailwindcss - shadow-cljs + tailwindcss-jit setup
- ertugrulcetin/racing-game-cljs - A 3D racing game built with ClojureScript, React and ThreeJS
- prestancedesign/pingcrm-clojure - A full stack SPA application built with Clojure + ClojureScript to illustrate how Inertia.js works.
- ... please let me know if you created something to include here
Libraries
- flexsurfer/rn-shadow-steroid - React Native with shadow-cljs on steroids
- re-frame-flow - A graph based visualization tool for re-frame event chains using shadow-cljs
- ... please let me know if you created something to include here
License
Copyright © 2024 Thomas Heller
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.