authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
With a Master’s in CS and mathematics, Luke specializes in functional programming. A Google internship launched his powerhouse dev career.
There are probably two main thoughts in your head while you read this introduction:
But wait! That’s where you’re wrong—and I will prove it to you. If you’re willing to invest 10 minutes of your time, I will show you how ClojureScript can make writing front-end and React-y applications fun, fast, and—most importantly—functional.
So you don’t have much time to learn ClojureScript, and you just want to see where this whole thing is going. First things first, what is ClojureScript?
From the ClojureScript website: ClojureScript is a compiler for Clojure that targets JavaScript. It emits JavaScript code which is compatible with the advanced compilation mode of the Google Closure optimizing compiler.
Amongst other things, ClojureScript has a lot to offer:
With that out of the way, let’s open up this can of worms with an example:
(defn component
[]
[:div
"Hello, world!"])
Note for those not familiar with Lisp dialects or ClojureScript: The most important parts of this example are the This is the most basic form of a React component in ClojureScript. That’s it—just a keyword, a string, and a whole bunch of lists. Pshaw! you say, that’s not significantly different from “hello world” in JSX or in TSX: However, there are some crucial differences that we can spot even from this basic example: These two small differences have huge consequences, not just in how you write code but in how you express yourself as well! How is that, you ask? Let’s jump into the fray and see what else ClojureScript has in store for us… Throughout this ClojureScript tutorial, I will endeavor to not dig too far into what it is that makes Clojure[Script] great (which is a lot of things, but I digress). Nonetheless, it will be useful to have some basic concepts covered so it is possible to grasp the breadth of what we can do here. For those experienced Clojuristas and Lispians, feel free to jump forward to the next section! There are three main concepts I will need to cover first: Clojure[Script] has a concept called a Keyword. It lies somewhere between a constant string (say, in Java) and a key. They are symbolic identifiers that evaluate to themselves. As an example, the keyword …in Clojure you would simply have: Also note: In Clojure, a map is both a collection (of keys to values, just like a Java Clojure[Script] being a Lisp dialect means that it places a lot of emphasis on lists. As I mentioned earlier, there are two main things to be aware of: To build a list of things in Clojure[Script], you do the following: For sequences, it’s a little different: The prepending Finally, we have functions. A function in Clojure[Script] is a sequence that is typed out without the prepending One corollary of this behavior is that you can build up a definition of a function without actually executing it! Only a ‘naked’ sequence will be executed when the program is evaluated. This will become relevant later! Functions can be defined in a few ways: So now that we’ve covered the basics, let’s drill into a little more detail to see what is happening here. React in ClojureScript is generally done using a library called Reagent. Reagent uses Hiccup and its syntax to represent HTML. From the Hiccup repo’s wiki: “Hiccup turns Clojure data structures like this:” “Into strings of HTML like this:” Simply put, the first element of the list becomes the HTML element type, and the remaining become the contents of that element. Optionally, you can provide a map of attributes which will then be attached to that element. Elements can be nested within each other simply by nesting them within the list of their parent! This is easiest seen with an example: Notice how we can put any old function or general Clojure syntax within our structure without having to explicitly declare an embedding method. It’s just a list, after all! And even better, what does this evaluate to at runtime? A list of keywords and contents of course! There are no funny types, no magic hidden methods. It’s just a plain old list of things. You can splice and play with this list as much as you want—what you see is what you’re getting. With Hiccup doing the layout and Reagent doing the logic and event processing, we end up with a fully functional React environment. All right, let’s tie this together a bit more with some components. One of the magical things about React (and Reagent) is that you compartmentalize your view and layout logic into modules, which you can then reuse throughout your application. Say we create a simple component that shows a button and some simple logic: Quick note on naming: Modules in Clojure are normally namespaced, so This simple component presents us with an optionally polite widget. Now let’s embed this within our Here we embed our widget within our app component by calling it as the first item of a list—just like the HTML keywords! We can then pass any children or parameters to the component by providing them as other elements of the same list. Here we simply pass If we were to evaluate our app function now, we would get the following: Note how Components within your DOM tree are only evaluated (and thus converted into real React objects behind the scenes) if they have been updated, which keeps things nice and snappy and reduces the amount of complexity that you have to deal with at any point in time. More detail on this subject for those interested can be obtained in the Reagent docs. Furthermore, note how the DOM is just a list of lists, and components are just functions that return lists of lists. Why is this so important as you learn ClojureScript? Because anything you can do to functions or lists, you can do to components. This is where you start to get compounding returns by using a Lisp dialect like ClojureScript: Your components and HTML elements become first-class objects that you can manipulate like any other normal data! Let me just say that again: Components and HTML elements are first-class supported objects within the Clojure language! That’s right, you heard me. It’s almost like Lisps were designed to process lists (hint: they were.) This includes things like: Dealing with plain old data types like lists and maps is dramatically simpler than anything resembling a class, and ends up being much more powerful in the long run! Okay, let’s recap. What has our ClojureScript tutorial shown so far? These two points fit snugly into the Clojure and functional programming ethos—code is data to be manipulated, and complexity is built up through connecting less complex parts. We present our program (our webpage in this example) as data (lists, functions, maps) and keep it that way until the very last moment when Reagent takes over and turns it into React code. This makes our code re-usable, and most importantly very easy to read and understand with very little magic. Now we know how to make an application with some basic functionality, so let’s move on to how we can make it look good. There are a couple of ways to approach this, the most simple being with style sheets and referencing their classes in your components: This will do exactly as you would expect, presenting us with a beautiful, red “Hello, world!” text. However, why go to all of this trouble colocating view and logic code into your components, but then separating out your styling into a style sheet—not only do you now have to look in two different places, but you’re dealing with two different languages too! Why not write our CSS as code in our components (see a theme here?). This will give us a bunch of advantages: My personal favorite flavor of CSS-in-code is with Clojure Style Sheets (cljss). Embedded CSS looks like the below: There are many other things that cljss can do for you (composing styles, animations, element overrides, etc.) that I won’t go into detail here. I recommend that you check it out yourself! Lastly, there’s the glue required to stick all of this together. Fortunately, besides a project file and an You need: You’ll find the full code example for this ClojureScript tutorial available on GitHub. So that’s it (for now). Hopefully, I have at least piqued your curiosity a small amount, whether that’s to check out a Lisp dialect (Clojure[Script] or otherwise) or even to try your hand at making your own Reagent application! I promise you that you won’t regret it. Join me in follow-up to this article, Getting Into a State, where I talk about state management using re-frame—say hello to Redux in ClojureScript!:div
, the []
, and the ()
. :div
is a keyword representing the []
is a vector, much like an ArrayList
in Java, and ()
is a sequence, much like a LinkedList
. I will touch on this in more detail later in this post!
function component() {
return (
Building Blocks
Keywords
:cat
will always refer to :cat
and never to anything else. Just as in Java you might say:private static const String MY_KEY = "my_key";
// ...
myMap.put(MY_KEY, thing);
// ...
myMap.get(MY_KEY);
(assoc my-map :my-key thing)
(my-map :my-key) ; equivalent to (:my-key my-map) ...nice and flexible!
HashMap
) and a function for accessing its contents. Neat!Lists
[]
is a vector, much like an ArrayList
.()
is a sequence, much like a LinkedList
.[1 2 3 4]
["hello" "world"]
["my" "list" "contains" 10 "things"] ; you can mix and match types
; in Clojure lists!
'(1 2 3 4)
'("hello" "world")
'
is explained in the next section.Functions
'
. The first element of that list is the function itself, and all the following elements will be the arguments. For example:(+ 1 2 3 4) ; -> 10
(str "hello" " " "world") ; -> "hello world"
(println "hi!") ; prints "hi!" to the console
(run-my-function) ; runs the function named `run-my-function`
(+ 1 1) ; -> 2
'(+ 1 1); -> a list of a function and two numbers
; A normal function definition, assigning the function
; to the symbol `my-function`
(defn my-function
[arg1 arg2]
(+ arg1 arg2))
; An anonymous function that does the same thing as the above
(fn [arg1 arg2] (+ arg1 arg2))
; Another, more concise variation of the above
#(+ %1 %2)
A Closer Examination
[:a {:href "http://github.com"} "GitHub"]
GitHub
[:div
[:h1 "This is a header"]
[:p "And in the next element we have 1 + 1"]
[:p (+ 1 1)]]
[:div
[:h1 "This is a header"]
[:p "And in the next element we have 1 + 1"]
[:p 2]]
A More Complicated Example
; widget.cljs
(defn component
[polite?]
[:div
[:p (str "Do not press the button" (when polite? ", please."))]
[:input {:type "button"
:value "PUSH ME"
:on-click #(js/alert "What did I tell you?")}]])
widget.cljs
might be imported under the namespace widget
. This means that the top level component
function will be accessed as widget/component
. I like to have only one top-level component per module, but this is a style preference—you might prefer to name your component function something like polite-component
or widget-component
.(when polite? ", please.")
evaluates to ", please."
when polite? == true
and to nil
when it is false
.app.cljs
:(defn app
[]
[:div
[:h1 "Welcome to my app"]
[widget/component true]])
true
, so in our widget polite? == true
, and thus we get the polite version.[:div
[:h1 "Welcome to my app"]
[widget/component true]] ; <- widget/component would look more like a
; function reference, but I have kept it
; clean for legibility.
widget/component
has not been evaluated! (See the Functions section if you’re confused.)Lists All the Way Down
(def words ["green" "eggs" "and" "ham"])
(defn li-shout
[x]
[:li (string/uppercase x))
(concat [:ol] (map li-shout words)
; becomes
[:ol
[:li "GREEN"]
[:li "EGGS"]
[:li "AND"]
[:li "HAM"]]
; in widget.cljs
(defn greeting-component
[name]
[:div
[:p (str "Hiya " name "!")]])
; ...
(def shouty-greeting-component
#(widget/greeting-component (string/uppercase %)))
(defn app
[]
[:div
[:h1 "My App"]
[shouty-greeting-component "Luke"]]) ; <- will show Hiya LUKE!
(def default-btn-attrs
{:type "button"
:value "I am a button"
:class "my-button-class"})
(defn two-button-component
[]
[:div
[:input (assoc default-btn-attrs
:on-click #(println "I do one thing"))]
[:input (assoc default-btn-attrs
:on-click #(println "I do a different thing"))]])
A Pattern Emerges
Getting Stylish
.my-class {
color: red;
}
[:div {:class "my-class"}
"Hello, world!"]
;; -- STYLES ------------------------------------------------------------
(defstyles component-style []
{:color "red"
:width "100%"})
;; -- VIEW --------------------------------------------------------------
(defn component
[]
[:div {:class (component-style)}
"Hello, world!"])
defstyles
creates a function which will generate a unique class name for us (which is great for anyone who imports our components.)Assembling the Pieces of a ClojureScript App
index.html
, boilerplate is at a minimum here.
project.clj
. A staple of any Clojure project, this defines your dependencies—even directly from GitHub—and other build properties (similar to build.gradle
or package.json
.)index.html
that acts as a binding point for the Reagent application.
React is a front-end development framework for JavaScript.
ClojureScript is a compiler for Clojure that targets JavaScript. It emits JavaScript code which is compatible with the advanced compilation mode of the Google Closure optimizing compiler. It inherits most of the properties of Clojure, a dynamic programming language supporting interactive development.
Lisp is the name used for a group of languages that share common characteristics (such as Clojure, Common Lisp, Scheme etc.), so “Lisp” itself is not really written. (This is like asking “What are the ingredients for a cake?” Most cakes follow a similar theme, but will have different ingredients.)
Yes! Lisp prioritizes the principle of “code as data.” This can be seen from the derivation of the name itself—LISt Processor. Everything in a Lisp dialect is data—even function calls are a list of arguments! Lisp is a fantastic style of programming language—there are many different flavors to suit any need.
Lisp dialects commonly invoke parts of functional and imperative programming languages. Clojure and ClojureScript are multiparadigm—they heavily lean on functional programming, and allow you to branch into imperative when it’s needed. This allows you to experience the best of both worlds!
The Google Closure Library is a broad and well-established cross-browser library for JavaScript, containing a variety of tools for UI, DOM manipulation, server communication, testing, etc. It is intended for use with the Google Closure Compiler.
The Google Closure Compiler compiles JavaScript into “better JavaScript” by manipulating, optimizing, and minifying to ensure that it downloads and runs faster. Additionally, it runs a suite of checks on your code to minimize problems at runtime.
With a Master’s in CS and mathematics, Luke specializes in functional programming. A Google internship launched his powerhouse dev career.
World-class articles, delivered weekly.
World-class articles, delivered weekly.
Join the Toptal® community.