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.
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.
Luke Tomlin
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:
- What is ClojureScript?
- 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:
-
[]
is a vector, much like anArrayList
. -
()
is a sequence, much like aLinkedList
.
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 tobuild.gradle
orpackage.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.
What is ClojureScript?
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.
What is Lisp written in?
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.)
Is Lisp any good?
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.
Is Lisp functional programming?
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!
What is the Google Closure Library?
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.
What is 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.