# The shadow-cljs quick start prints its own version as a placeholder

> shadow-cljs compiles ClojureScript through one EDN file and a package manager, with targets from :browser to :react-native. Its quick start is a transcript whose version line was never filled in, the tutorial stops at the words To be continued, and the tool's own repository carries three build descriptors, two CI systems and a committed out directory.

**thheller/shadow-cljs** — ClojureScript compilation made easy

- Repository: https://github.com/thheller/shadow-cljs
- Website: https://github.com/thheller/shadow-cljs
- Stars: 2,407 · Forks: 193
- Language: Clojure
- License: EPL-1.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/thheller-shadow-cljs

## The transcript in the quick start was never filled in

The quick start is a single command and the output it claims to produce.

```bash
$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
```

The interesting line is `shadow-cljs@<version>`. The angle brackets were never substituted, so the one number a reader would most want, the version of the compiler that a fresh project receives, is a placeholder in the documentation.

The rest of the transcript is a real run: five files and two directories created, a package lockfile the output asks you to commit, 88 packages installed, 636 audited and zero vulnerabilities reported. The scaffold it produces is small on purpose: `package.json`, `package-lock.json`, `shadow-cljs.edn` and a `src` directory holding `main` and `test`.

## One EDN file for builds, package.json for everything else

Two files carry two different kinds of configuration, and the README is explicit about which is which.

`shadow-cljs.edn` configures the ClojureScript builds and the ClojureScript dependencies. `package.json` is used by npm to manage the JavaScript dependencies. Nothing else in the scaffold is configuration, which is the point of the defaults the project advertises.

The build itself is a map under `:builds`, where each key is a build name and each value carries a target and its modules.

```clojure
{...
 :builds
 {:frontend
  {:target :browser
   :modules {:main {:init-fn acme.frontend.app/init}}
   }}}
```

The `:init-fn` is how a ClojureScript entry point reaches the generated JavaScript: the compiler calls that function when the code loads, so the browser side has a single named function to invoke.

The repository that holds the tool itself keeps three build descriptors side by side: `deps.edn` for the Clojure CLI, `project.clj` for Leiningen, and `shadow-cljs.edn` for its own configuration. Two of the three are there to build the tool in whichever way the contributor prefers.

## Java 21 is the floor and the targets are keywords

The requirements are short and specific: Node.js or Bun, a package manager from npm, bun, pnpm or yarn, and a Java SDK at version 21 or newer with the latest LTS recommended. The Java floor is the one that bites, because a default JDK on an older machine will not satisfy it.

The supported targets are keywords in the same file: `:browser`, `:node-script`, `:npm-module`, `:react-native` and `:chrome-extension`, with more implied by the ellipsis. Code splitting is done through `:modules`, which is the same key the quick start uses for its single entry point, so a project can go from one module to several without leaving the configuration format.

Two development conveniences are listed as features rather than as add ons: live reload for both ClojureScript and CSS, and a REPL. The REPL is started by name, `npx shadow-cljs node-repl` for a node process and `npx shadow-cljs browser-repl` for the browser, which means you can be in a REPL before any build is configured.

## Namespaces are expected to mirror Java package paths

The default source path is `src/main`, and from there the guidance gets specific about naming.

You are told to follow the Java Naming Conventions when organising ClojureScript namespaces, and to start every namespace with a prefix specific enough to avoid collisions, a company name or a project name, rather than something generic like `app.core`. The worked example is a frontend for a fictional company built as `acme.frontend.app`, on the reasoning that it can grow to cover `acme.backend.*` later without renaming.

The consequence is a file layout on disk. For `acme.frontend.app` the expected filename is `src/main/acme/frontend/app.cljs`, so the namespace path becomes a directory path under the source root.

The sample file is three lines of code: a namespace declaration and a single `init` function that prints Hello World. It exists to give the compiler something to compile, not to demonstrate the language, and the build config in the next section exists to give it somewhere to be called from.

## The dev server is a configuration key, not a flag

Because browsers restrict what can be loaded from disk, the tutorial needs a server, and shadow-cljs provides one through configuration.

```clojure
{...
 :dev-http {8080 "public"}
 :builds
 {:frontend
  {:target :browser
   :modules {:main {:init-fn acme.frontend.app/init}}
   }}}
```

Adding `:dev-http` with port 8080 mapped to the `public` directory is enough. Once the configuration is saved the server starts on its own and serves the content at `http://localhost:8080`, with no need to restart the running process. Opening that URL and looking at the browser console is the first checkpoint, and the expected result is Hello World.

The output directory is on the same default track. No `:output-dir` is configured in the build, so the default `public/js` applies, and the module named `:main` becomes `main.js` inside it, which is what the `public/index.html` file loads with a script tag pointing at `/js/main.js`.

The README also says you can use any server you like at that point, since the requirement is that files from `public` are served properly.

## The tutorial ends with the words To be continued

The quick start finishes with `To be continued ...`, which is an accurate description of the state of the documentation.

The user manual is linked as the reference and is labelled work in progress. So the depth lives outside the repository: three video courses on Reagent, on Reframe and on building an application for React developers, a set of community guides in English and Chinese including a 2.x tutorial, and worked examples maintained separately, among them an official browser example, a Leiningen template for a Reframe project, and a Reframe usage example.

Two of those links are worth naming as category rather than content. A browser extension repository is linked from the header, which is the Chrome extension target in practice. And a Slack channel is where the project asks questions to be asked.

So the README is an onboarding path plus an index, and the project itself acknowledges that, which is more useful to a reader than a longer page that stopped pretending to be complete.

## Three build descriptors, two CI systems and a committed out directory

The repository that builds the tool is busier than the tool it builds.

Three build descriptors are at the root, `deps.edn`, `project.clj` and `shadow-cljs.edn`, and three shell scripts sit beside them, `build-all.sh`, `build-cli.sh` and `build-deps.sh`, with `start-dev.sh` and `container-start-dev.sh` for running it. Containerization has a `Containerfile`, and there are two CI systems, a `.circleci/` directory and a `.github/` directory.

Two entries tell you about how the project is tested and shipped. `out/` appears in the root listing, so build output is committed rather than generated on demand. And `karma.conf.js` sits next to `test/`, `test-env/` and `test-project/`, which means the JavaScript tests run in a browser harness while the Clojure tests live in a normal tree.

For its own documentation and examples the project uses `postcss.config.js` and `tailwind.config.js`, so the Tailwind and PostCSS path is exercised inside the tool's repository rather than only described.

`externs/` is the other entry with a technical name worth recognising, since Closure externs are how the compiler is told about globals it cannot see.

## A private manifest with no scripts and four dependencies

The root `package.json` is not the package you install. It is marked private, its scripts object is empty, and it lists four dependencies.

Those four explain what the tool needs to run in node: `jsdom` so that browser APIs exist in a node process, `ws` for the websocket connection the dev server and live reload rely on, `readline-sync` for synchronous terminal input in the REPL, and `source-map-support` so stack traces map back to the original ClojureScript.

The version skew is visible in one line. `jsdom` is pinned to a 24 series release while `ws` is still on 7, so a browser emulation from the current era sits next to a websocket library several majors behind. That combination is a maintenance decision by the maintainer rather than a mistake, and it is the kind of thing worth knowing before you report a stack trace problem.

On the outside, the distribution channels are npm and Clojars, both linked at the top of the README. There are no GitHub releases, so `CHANGELOG.md` is the version record, and the licence is EPL-1.0. The last push was on 2026-10-01 and the repository is not archived.

## Conclusion

Adopt shadow-cljs if you want ClojureScript with a REPL, live reload and npm integration without assembling a toolchain yourself, and accept that the configuration is one EDN file whose defaults do most of the work. Do not adopt it expecting the documentation to be finished, because the quick start itself ends mid tutorial and the user manual is labelled work in progress; the third party guides and video courses are where the depth is. Check three things first: that your Java is 21 or newer, which is the stated floor; that your package manager is one of npm, bun, pnpm or yarn, since all four are supported and the generated lockfile is npm shaped; and that you are reading a version that matches your project, since the repository publishes no GitHub releases and the quick start transcript shows the version as an unsubstituted placeholder.

## FAQ

### what is shadow cljs

A build tool that compiles ClojureScript, configured through a shadow-cljs.edn file and installed from npm or Clojars. It supplies configuration defaults, integrates with npm, offers live reload for ClojureScript and CSS and a REPL, and targets :browser, :node-script, :npm-module, :react-native and :chrome-extension.

### What does shadow-cljs need installed?

Node.js or Bun, one of npm, bun, pnpm or yarn, and a Java SDK at version 21 or newer, with the latest LTS release recommended. That Java floor is the requirement most likely to be missing on an existing machine.

### How do I start a new shadow-cljs project?

Run npx create-cljs-project with a project name. The installer creates package.json, shadow-cljs.edn, .gitignore and src/main plus src/test, installs the latest shadow-cljs into the project, and produces a package-lock.json that the output tells you to commit.

### Where does shadow-cljs put the compiled JavaScript?

In the build's :output-dir, which defaults to public/js when none is set, so the module named :main becomes public/js/main.js. Adding :dev-http with 8080 mapped to public starts the built-in server at http://localhost:8080 as soon as the configuration is saved, with no restart needed.

### How can I tell which shadow-cljs version I have?

The repository publishes no GitHub releases, so the changelog is the record, and the quick start transcript prints the installed version as an unsubstituted placeholder. The project is distributed through npm and Clojars, so the version in your project lockfile is what identifies your compiler.

## Sources

- [Issues](https://github.com/thheller/shadow-cljs/issues)
- [License: EPL-1.0](https://github.com/thheller/shadow-cljs/blob/master/LICENSE)
- [Project website](https://github.com/thheller/shadow-cljs)
- [README](https://github.com/thheller/shadow-cljs/blob/master/README.md)
- [thheller/shadow-cljs on GitHub](https://github.com/thheller/shadow-cljs)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/thheller-shadow-cljs
