← Back to clojure/clojurescript

How to Deploy & Use clojure/clojurescript

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

  1. Create a new project directory:

    mkdir my-cljs-app
    cd my-cljs-app
    
  2. Create a deps.edn file: 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.
  3. Create your source directory and a ClojureScript file:

    mkdir src/my_cljs_app
    touch src/my_cljs_app/core.cljs
    
  4. 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

  1. Create a new project:

    lein new figwheel-main my-cljs-app -- --reagent
    cd my-cljs-app
    

    (Using figwheel-main template for a common modern setup, but you can use lein new cljs-app for a simpler one).

  2. Verify project.clj: Ensure org.clojure/clojurescript is 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

  1. Create a pom.xml file:
    <!-- 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):

  1. 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"]}}}
    
  2. Run the development build:

    clj -A:dev-build
    
  3. Run with watch mode (for automatic recompilation on file changes):

    clj -A:watch-dev
    
  4. 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.html in 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.

  1. 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"]}}}
    
  2. Run the production build:

    clj -A:prod-build
    

    This 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.

  1. Start development server with hot-reloading:

    lein figwheel
    

    This will compile your ClojureScript and serve it, typically at http://localhost:9500. Changes to your .cljs files will automatically recompile and update the browser.

  2. Compile for production:

    lein do clean, cljsbuild once min
    

    (The exact command might vary based on your project.clj and cljsbuild plugin configuration).

5. Deployment

ClojureScript compiles to standard JavaScript, making deployment straightforward to any platform that can serve static web assets.

  1. Build your application for production:

    clj -A:prod-build # or lein do clean, cljsbuild once min
    

    This will generate your optimized JavaScript bundle (e.g., out/main.min.js) and potentially other assets.

  2. 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/).

  3. 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.

Example: Deploying to Netlify

  1. Ensure your public/ directory contains index.html and main.min.js.
  2. Commit your project to a Git repository (GitHub, GitLab, Bitbucket).
  3. Sign in to Netlify, create a new site, and link it to your repository.
  4. Configure build settings:
    • Build command: clj -A:prod-build (or your Leiningen/Maven equivalent)
    • Publish directory: out (or public, depending on where your build output goes)
  5. 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 clojure command is available, or that your deps.edn/project.clj correctly 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"]) or project.clj (:source-paths ["src"]).
      • Ensure the filename matches the namespace (e.g., my_cljs_app/core.cljs for my-cljs-app.core).
      • Check for typos in the --main or --compile argument.
  • 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 advanced optimizations, you might need to use ^:externs or an externs.js file 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.
  • Advanced compilation breaks my code:

    • Cause: Google Closure Compiler's advanced optimizations are aggressive and can break code that isn't written with them in mind, especially when interacting with external JavaScript.
    • Solution:
      • Use ^:export metadata on functions/variables you want to expose to external JavaScript.
      • Provide externs.js files 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 none or simple optimizations and gradually move to advanced once your code is stable.
  • 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.