ClojureScript Deployment and Usage Guide
ClojureScript is a compiler for Clojure that targets JavaScript, designed to emit JavaScript code compatible with the Google Closure optimizing compiler.
1. Prerequisites
To use ClojureScript, you will need:
- Java Development Kit (JDK) 8 or higher: Clojure and ClojureScript run on the Java Virtual Machine.
- Clojure CLI Tools or Leiningen or Maven: These are build automation tools for Clojure projects. Clojure CLI Tools are recommended for new projects.
2. Installation
ClojureScript is typically used as a dependency in your project rather than a standalone installation.
2.1 Project Setup with Clojure CLI Tools
-
Create a new project directory:
mkdir my-cljs-app cd my-cljs-app -
Create a
deps.ednfile: This file defines your project's dependencies.;; deps.edn {:deps {org.clojure/clojure {:mvn/version "1.11.1"} ; Or your preferred Clojure version org.clojure/clojurescript {:mvn/version "1.12.134"}} :paths ["src" "resources"] :aliases {:build {:main-opts ["-m" "cljs.main" "--compile" "my-cljs-app.core" "--output-to" "out/main.js"]}}}org.clojure/clojure: The core Clojure library.org.clojure/clojurescript: The ClojureScript compiler.my-cljs-app.core: Replace with the namespace of your main ClojureScript entry point.out/main.js: The output path for your compiled JavaScript.
-
Create your source directory and a ClojureScript file:
mkdir src/my_cljs_app touch src/my_cljs_app/core.cljs -
Add basic ClojureScript code to
src/my_cljs_app/core.cljs:;; src/my_cljs_app/core.cljs (ns my-cljs-app.core) (defn ^:export greet [name] (str "Hello, " name "!")) (println (greet "ClojureScript"))
2.2 Project Setup with Leiningen
-
Create a new project:
lein new figwheel-main my-cljs-app -- --reagent cd my-cljs-app(Using
figwheel-maintemplate for a common modern setup, but you can uselein new cljs-appfor a simpler one). -
Verify
project.clj: Ensureorg.clojure/clojurescriptis listed in:dependencies.;; project.clj (excerpt) :dependencies [[org.clojure/clojure "1.11.1"] [org.clojure/clojurescript "1.12.134"] ;; ... other dependencies ]
2.3 Project Setup with Maven
- Create a
pom.xmlfile:<!-- pom.xml --> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>my-cljs-app</artifactId> <version>0.1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <clojure.version>1.11.1</clojure.version> <clojurescript.version>1.12.134</clojurescript.version> </properties> <dependencies> <dependency> <groupId>org.clojure</groupId> <artifactId>clojure</artifactId> <version>${clojure.version}</version> </dependency> <dependency> <groupId>org.clojure</groupId> <artifactId>clojurescript</artifactId> <version>${clojurescript.version}</version> </dependency> </dependencies> <!-- You'll typically need a plugin to run ClojureScript compilation, e.g., clojure-maven-plugin or a custom execution. --> </project>- Maven setup for ClojureScript is less common than Leiningen or Clojure CLI. You'll likely need to configure a plugin to invoke the ClojureScript compiler.
3. Configuration
ClojureScript compilation is primarily configured through build tool options.
3.1 Compiler Options
The ClojureScript compiler accepts various options to control its behavior. These are typically passed via your build tool.
Common options include:
--output-to <file>: Specifies the output JavaScript file.--output-dir <directory>: Specifies the output directory for compiled JavaScript files.--optimizations <level>: Sets the optimization level (none,whitespace,simple,advanced).--pretty-print: Prints generated JavaScript in a human-readable format.--verbose: Enables verbose output during compilation.--watch <directory>: Watches a directory for changes and recompiles.--main <namespace>: Specifies the main namespace to compile.--asset-path <path>: Path to assets (e.g., for source maps).
Refer to the official ClojureScript Compiler Options documentation for a complete list.
4. Build & Run
4.1 Development Build (Clojure CLI Tools)
To compile your ClojureScript code for development (e.g., with no optimizations and source maps):
-
Add a build alias to
deps.edn:;; deps.edn (excerpt) :aliases {:dev-build {:main-opts ["-m" "cljs.main" "--compile" "my-cljs-app.core" "--output-to" "out/main.js" "--output-dir" "out" "--asset-path" "out" "--verbose" "--pretty-print" "--optimizations" "none" "--source-map" "out/main.js.map"]} :watch-dev {:main-opts ["-m" "cljs.main" "--watch" "src" "--compile" "my-cljs-app.core" "--output-to" "out/main.js" "--output-dir" "out" "--asset-path" "out" "--verbose" "--pretty-print" "--optimizations" "none" "--source-map" "out/main.js.map"]}}} -
Run the development build:
clj -A:dev-build -
Run with watch mode (for automatic recompilation on file changes):
clj -A:watch-dev -
Include the compiled JavaScript in an HTML file:
<!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>My ClojureScript App</title> </head> <body> <div id="app"></div> <script src="out/main.js"></script> </body> </html>Open
index.htmlin your browser and check the developer console for the "Hello, ClojureScript!" output.
4.2 Production Build (Clojure CLI Tools)
For production, you typically want advanced optimizations for smaller file size and better performance.
-
Add a production build alias to
deps.edn:;; deps.edn (excerpt) :aliases {;; ... existing aliases :prod-build {:main-opts ["-m" "cljs.main" "--compile" "my-cljs-app.core" "--output-to" "out/main.min.js" "--optimizations" "advanced" "--pseudo-names" "false" ; Recommended for advanced compilation "--pretty-print" "false" "--verbose"]}}} -
Run the production build:
clj -A:prod-buildThis will generate
out/main.min.js.
4.3 Build & Run with Leiningen
If you used a template like figwheel-main, it will come with pre-configured tasks.
-
Start development server with hot-reloading:
lein figwheelThis will compile your ClojureScript and serve it, typically at
http://localhost:9500. Changes to your.cljsfiles will automatically recompile and update the browser. -
Compile for production:
lein do clean, cljsbuild once min(The exact command might vary based on your
project.cljandcljsbuildplugin configuration).
5. Deployment
ClojureScript compiles to standard JavaScript, making deployment straightforward to any platform that can serve static web assets.
-
Build your application for production:
clj -A:prod-build # or lein do clean, cljsbuild once minThis will generate your optimized JavaScript bundle (e.g.,
out/main.min.js) and potentially other assets. -
Gather static assets: Collect your
index.html,main.min.js, and any other static assets (CSS, images, fonts) into a single directory (e.g.,public/). -
Choose a deployment platform:
- Static Site Hosting (e.g., Netlify, Vercel, GitHub Pages, AWS S3 + CloudFront): Ideal for purely client-side ClojureScript applications. Upload your
public/directory. - Node.js Server (e.g., Express.js): If your application has a Node.js backend. Serve the
public/directory as static files. - JVM Server (e.g., Ring/Compojure): If your application has a Clojure backend. Place your compiled JavaScript and HTML in the appropriate resources directory and serve them.
- Containerization (e.g., Docker): Package your application (e.g., a Node.js or JVM server serving static files) into a Docker image and deploy to platforms like Kubernetes, AWS ECS, Google Cloud Run, etc.
- Static Site Hosting (e.g., Netlify, Vercel, GitHub Pages, AWS S3 + CloudFront): Ideal for purely client-side ClojureScript applications. Upload your
Example: Deploying to Netlify
- Ensure your
public/directory containsindex.htmlandmain.min.js. - Commit your project to a Git repository (GitHub, GitLab, Bitbucket).
- Sign in to Netlify, create a new site, and link it to your repository.
- Configure build settings:
- Build command:
clj -A:prod-build(or your Leiningen/Maven equivalent) - Publish directory:
out(orpublic, depending on where your build output goes)
- Build command:
- Netlify will automatically build and deploy your application on every push to your main branch.
6. Troubleshooting
-
java.lang.ClassNotFoundException: clojure.main:- Cause: Clojure is not correctly set up or found in your classpath.
- Solution: Ensure Java is installed and
clojurecommand is available, or that yourdeps.edn/project.cljcorrectly specifies Clojure as a dependency.
-
No such namespace: my-cljs-app.core:- Cause: The compiler cannot find your main ClojureScript file or namespace.
- Solution:
- Verify the path in your
deps.edn(:paths ["src"]) orproject.clj(:source-paths ["src"]). - Ensure the filename matches the namespace (e.g.,
my_cljs_app/core.cljsformy-cljs-app.core). - Check for typos in the
--mainor--compileargument.
- Verify the path in your
-
ReferenceError: <some-js-library> is not defined:- Cause: You're trying to use a JavaScript library that hasn't been properly included or exposed to ClojureScript.
- Solution:
- Ensure the JavaScript library is loaded before your ClojureScript output in your
index.html. - If using
advancedoptimizations, you might need to use^:externsor anexterns.jsfile to prevent the Google Closure Compiler from renaming or removing symbols that your ClojureScript code depends on. - For Node.js modules, you might need a bundler like Webpack or Rollup, or use
shadow-cljs.
- Ensure the JavaScript library is loaded before your ClojureScript output in your
-
Advanced compilation breaks my code:- Cause: Google Closure Compiler's
advancedoptimizations are aggressive and can break code that isn't written with them in mind, especially when interacting with external JavaScript. - Solution:
- Use
^:exportmetadata on functions/variables you want to expose to external JavaScript. - Provide
externs.jsfiles for any external JavaScript libraries your ClojureScript code interacts with. These files declare the global variables and properties that the compiler should not rename. - Start with
noneorsimpleoptimizations and gradually move toadvancedonce your code is stable.
- Use
- Cause: Google Closure Compiler's
-
Slow compilation times:
- Cause: Large project, complex macros, or insufficient system resources.
- Solution:
- Ensure you have enough RAM allocated to the JVM.
- Use a tool like Figwheel Main or Shadow-CLJS, which provide fast incremental compilation and hot code reloading for development.
- For production builds, ensure you're not compiling unnecessary namespaces.
-
Browser console shows
Uncaught TypeError: Cannot read properties of undefined (reading 'call')or similar:- Cause: Often related to incorrect Google Closure Compiler externs or issues with interop between ClojureScript and JavaScript.
- Solution: Double-check your externs, especially for third-party libraries. Ensure you're not trying to call a function or access a property that the compiler has renamed or removed.