DEV Community

Maksim
Maksim

Posted on • Originally published at maksimrv.Medium on

Setup shadow-cljs react project

🚀 Create project

The first step is create a project

npx create-cljs-project my-project
Enter fullscreen mode Exit fullscreen mode

This command will create a project my-project with minimal required structure and install required dev dependencies for proper work of shadow-cljs

├── package-lock.json
├── package.json
├── shadow-cljs.edn
└── src
    ├── main
    └── test
Enter fullscreen mode Exit fullscreen mode

shadow-cljs.edn is a configuration file for ClojureScript project

;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

:dependencies
 []

:builds
 {}}
Enter fullscreen mode Exit fullscreen mode

♺ Compile ClojureScript to JavaScript

Now, let’s create an entry point ClojureScript file

touch ./src/main/core.cljs
Enter fullscreen mode Exit fullscreen mode

and add minimal boilerplate code to it

;; ./src/main/core.cljs
(ns core)

(defn main []
  (js/console.log "Hello World!"))

(main)
Enter fullscreen mode Exit fullscreen mode

Now we should specify how to build the project. To do this, we should modify the :builds section in shadow-cljs.edn file

...

:builds
 {: **app** {:target : **browser**
        :modules {: **main** {:entries [**core**]}}}}}
Enter fullscreen mode Exit fullscreen mode
  • :app is a build id
  • :browser is target’s type for generated JavaScript files
  • :main is a name of generated JavaScript file
  • core is a ClojureScript entry namespace

You can add :output-dir "public/js" to explicitly specify output directory for generated files by default shadow-cljs will use public/js

Now we can compile our ClojureScript to JavaScript files

npx shadow-cljs compile **app**

shadow-cljs - config: /private/tmp/my-project/shadow-cljs.edn
shadow-cljs - connected to server
[:app] Compiling ...
[:app] Build completed. (43 files, 0 compiled, 0 warnings, 0.33s)
Enter fullscreen mode Exit fullscreen mode

đź‘ľ Start server

Now we are ready to setup the development server which will serve our static files. For this, we should return back to shadow-cljs.edn and add information about the dev server

:dev-http {8000 "public"}
Enter fullscreen mode Exit fullscreen mode
  • 8000 is a port which will be used by http server
  • public is a folder name for static files

Now we can start it by using watch command

npx shadow-cljs **watch** app
Enter fullscreen mode Exit fullscreen mode

Beside the start http server watchcommand also automatically recompile JavaScript files when we change ClojureScript one

You can open http://localhost:8000 and see that the server is running

Not found. Missing index.html.
Enter fullscreen mode Exit fullscreen mode

but complains on missing index.html. Let’s add index.html to the public folder

touch ./public/index.html
Enter fullscreen mode Exit fullscreen mode

and add some minimal markup with reference to our generated JavaScript file

<!DOCTYPE html>
<html lang="">
  <head>
    <title>my-project</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <div id="app"></div>
    <script src=" **js/main.js**" type="text/javascript"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

If we open developer tools in the browser we should see “Hello World!” message in the browser’s console. You can open core.cljs file and change the message. Browser automatically pickup the change via embedded to shadow-cljs live update functionality and display the new message

📦 Add React as dependency

shadow-cljs support npm dependencies, so here we can install react via npm

npm install react
Enter fullscreen mode Exit fullscreen mode

Now we can use React in our ClojureScript application. Let’s print React’s version in the browser’s console. To do this, let’s modify core.cljs file

;; ./src/main/core.cljs
(ns core **(:require ["react" :as react])**)

(defn main []
  (js/console.log **(.-version react)**))

(main)
Enter fullscreen mode Exit fullscreen mode

We can look at the console without restarting the server and see that now it displays React version

[Log] shadow-cljs: load JS – "node_modules/react/index.js"
[Log] shadow-cljs: load JS – "core.cljs"
[Log] 17.0.2
Enter fullscreen mode Exit fullscreen mode

By the same way, we can install react-dom and render “Hello World!”

(ns core (:require 
           ["react" :as react]
           **["react-dom" :as react-dom]**))

(defn main []
  **(let [app-node (.getElementById js/document "app")]  
 (.render react-dom "Hello World!" app-node))** )

(main)
Enter fullscreen mode Exit fullscreen mode

🎸 Reagent

We can use raw react and react-dom in our ClojureScript but it’s much better and less verbose to use Reagent is a minimalistic ClojureScript interface for React.

Because Reagent is a ClojureScript module, we can’t install it via npm. To install it, we should define required version in shadow-cljs.edn

:dependencies
 [**[reagent "1.1.1"]**]
Enter fullscreen mode Exit fullscreen mode

Reagent is dependent from React & React DOM packages, so you still should install them via NPM.

Let’s update core.cljs code and replace React on Reagent

;; ./src/main/core.cljs
(ns core (:require 
          **[reagent.core :as r]  
 [reagent.dom :as rdom]** ))

**(defn app []  
 [:b "Hello World!"])**  

(defn main []
  (let [app-node (.getElementById js/document "app")]
    **(rdom/render [app] app-node)**))

(main)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)