Technology10 minute read

Unearthing ClojureScript for Front-end Development

ClojureScript and its multiparadigm approach (with an emphasis on functional programming) may sound esoteric to some front-end developers. But it’s amazing to work with, and getting started with it is far simpler than one might imagine. In this tutorial, Toptal Freelance Clojure Developer Luke Tomlin shows off some key features, getting straight into a React demo using Reagent and the Hiccup syntax.


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

ClojureScript and its multiparadigm approach (with an emphasis on functional programming) may sound esoteric to some front-end developers. But it’s amazing to work with, and getting started with it is far simpler than one might imagine. In this tutorial, Toptal Freelance Clojure Developer Luke Tomlin shows off some key features, getting straight into a React demo using Reagent and the Hiccup syntax.


Toptalauthors 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.
Luke Tomlin

Luke Tomlin

With a Master’s in CS and mathematics, Luke specializes in functional programming. A Google internship launched his powerhouse dev career.

Share

There are probably two main thoughts in your head while you read this introduction:

  1. What is ClojureScript?
  2. This isn’t relevant to my interests.

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.

Some ClojureScript Tutorial Prerequisites

  • Lisp knowledge is not required. I will do my best to explain any code samples that are scattered within this blog post!
  • However, if you do want to do a little pre-reading, I would highly recommend https://www.braveclojure.com/, the one-stop shop for getting started with Clojure (and by extension, ClojureScript).
  • Clojure and ClojureScript share a common language—I will often refer to these at the same time as Clojure[Script].
  • I do assume that you have knowledge of React and general front-end know-how.

How to Learn ClojureScript: The Short Version

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:

  • It’s a multiparadigm programming language with a functional programming lean—functional programming is known to improve code legibility as well as help you write more with less code.
  • It supports immutability by default—say goodbye to a whole suite of runtime issues!
  • It’s data-oriented: code is data in ClojureScript. Most Clojure[Script] applications can be reduced to a set of functions that operate on some underlying data structure, which makes debugging simple and code super-legible.
  • It’s simple! Getting started with ClojureScript is easy—there are no fancy keywords and very little magic.
  • It has a fantastic standard library. This thing has everything.

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 :div, the [], and the (). :div is a keyword representing the <div> element. [] 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!

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:

function component() {
  return (
    <div>
      "Hello, world!"
    </div>
  );
}

However, there are some crucial differences that we can spot even from this basic example:

  • There are no embedded languages; everything within the ClojureScript example is either a string, a keyword, or a list.
  • It is concise; lists provide all of the expressivity we need without the redundancy of HTML closing tags.

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…

Building Blocks

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:

Keywords

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 :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);

…in Clojure you would simply have:

(assoc my-map :my-key thing)
(my-map :my-key) ; equivalent to (:my-key my-map) ...nice and flexible!

Also note: In Clojure, a map is both a collection (of keys to values, just like a Java HashMap) and a function for accessing its contents. Neat!

Lists

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:

  1. [] is a vector, much like an ArrayList.
  2. () is a sequence, much like a LinkedList.

To build a list of things in Clojure[Script], you do the following:

[1 2 3 4]
["hello" "world"]
["my" "list" "contains" 10 "things"] ; you can mix and match types 
                                     ; in Clojure lists!

For sequences, it’s a little different:

'(1 2 3 4)
'("hello" "world")

The prepending ' is explained in the next section.

Functions

Finally, we have functions. A function in Clojure[Script] is a sequence that is typed out without the prepending '. 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`

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.

(+ 1 1) ; -> 2
'(+ 1 1); -> a list of a function and two numbers

This will become relevant later!

Functions can be defined in a few ways:

; 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

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:”

[:a {:href "http://github.com"} "GitHub"]

“Into strings of HTML like this:”

<a href="http://github.com">GitHub</a>

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:

[:div
  [:h1 "This is a header"]
  [:p "And in the next element we have 1 + 1"]
  [:p (+ 1 1)]]

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?

[:div
  [:h1 "This is a header"]
  [:p "And in the next element we have 1 + 1"]
  [:p 2]]

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.

A More Complicated Example

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:

; 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?")}]])

Quick note on naming: Modules in Clojure are normally namespaced, so 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.

This simple component presents us with an optionally polite widget. (when polite? ", please.") evaluates to ", please." when polite? == true and to nil when it is false.

Now let’s embed this within our app.cljs:

(defn app
  []
  [:div
   [:h1 "Welcome to my app"]
   [widget/component true]])

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 true, so in our widget polite? == true, and thus we get the polite version.

If we were to evaluate our app function now, we would get the following:

[: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.

Note how widget/component has not been evaluated! (See the Functions section if you’re confused.)

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.

Lists All the Way Down

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:

  • Mapping over elements of a numbered list:
(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"]]
  • Wrapping components:
; 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!
  • Injecting attributes:
(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"))]])

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!

A Pattern Emerges

Okay, let’s recap. What has our ClojureScript tutorial shown so far?

  • Everything is reduced down to the simplest functionality—elements are just lists, and components are just functions that return elements.
  • Because components and elements are first-class objects, we are able to write more with less.

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.

Getting Stylish

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:

.my-class {
  color: red;
}
[:div {:class "my-class"}
  "Hello, world!"]

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:

  • Everything that defines a component is in the same place.
  • Class names can be guaranteed unique through clever generation.
  • CSS can be dynamic, changing as our data changes.

My personal favorite flavor of CSS-in-code is with Clojure Style Sheets (cljss). Embedded CSS looks like the below:

;; -- 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.)

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!

Assembling the Pieces of a ClojureScript App

Lastly, there’s the glue required to stick all of this together. Fortunately, besides a project file and an index.html, boilerplate is at a minimum here.

You need:

  • Your project definition file, 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.)
  • An index.html that acts as a binding point for the Reagent application.
  • Some setup code for a dev environment and finally for starting up your Reagent application.

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!

Understanding the basics

  • Is React front-end or back-end?

    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.

Hire a Toptal expert on this topic.
Hire Now

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

Join the Toptal® community.