Writing HTML in Clojure
ClojureScript is a powerful language that allows you to write JavaScript applications using the concise and expressive syntax of Clojure. One of the common tasks when building web applications is generating HTML. In ClojureScript, there are a few different approaches to doing this, from directly embedding HTML in ClojureScript files to using libraries that provide more structured and maintainable methods.
This post will introduce you to writing HTML in ClojureScript and explore a popular library, Reagent, which makes it easy to work with React components in ClojureScript.
HTML as Data
At its core, HTML is structured data, and Clojure’s syntax lends itself very well to representing this data. In fact, you can represent HTML elements as simple vectors, where the first item is the tag name and subsequent items represent attributes and children.
Simple Example of HTML as Clojure Data:
[:div {:class "container"}
[:h1 "Welcome to Tic-Tac-Toe"]
[:p "This is a fun game built with ClojureScript!"]]
In this example:
- The :div tag contains attributes like :class, and it’s childeren are other HTML elements such as h1 and p tags.
- You can nest vectors inside each other to reflect the HTML structure you want to generate.
The vanilla HTML equivalent would look like this:
<div class="container">
<h1>Welcome to Tic-Tac-Toe</h1>
<p>This is a fun game built with ClojureScript!</p>
</div>
Enter Reagent: A ClojureScript Interface for React
While you can write raw HTML vectors as seen above, libraries like Reagent simplify the process. Reagent is a minimalistic wrapper around React that allows you to build dynamic UIs in ClojureScript.
Setting up Reagent
First, make sure you have Reagent in your project dependencies. Add the following to your project.clj file:
:dependencies [[reagent "1.0.0"]]
With Reagent, you can represent your HTML structure and UI components as Clojure functions that return vectors. Reagent takes care of converting these vectors into React elements behind the scenes.
Writing HTML with Reagent
Here’s a simple example of how to write HTML in ClojureScript using Reagent:
(ns my-app.core
(:require [reagent.core :as r]))
(defn home-page []
[:div
[:h1 "Welcome to My App"]
[:p "This is written in ClojureScript using Reagent."]])
(defn mount-root []
(r/render [home-page] (.getElementById js/document "app")))
(defn init []
(mount-root))
home-pageis a function that returns a vector representing HTML content.- The
[:div],[:h1], and[:p]elements are just like writing HTML, but they are Clojure vectors. mount-rootrenders the Reagent component into the DOM using the app element as the root.
Adding Interactivity
Reagent makes it easy to create dynamic UIs by leveraging Reagent atoms, which are reactive atoms that trigger UI
updates when their value changes. If you are familiar with react, they are similar to the UseState hook.
Here’s how you can add a simple button that updates a counter:
(defonce counter (r/atom 0))
(defn counter-component []
[:div
[:p "Counter: " @counter]
[:button {:on-click #(swap! counter inc)} "Increment"]])
(defn mount-root []
(r/render [counter-component] (.getElementById js/document "app")))
- We define a reactive atom counter initialized to 0.
- The counter-component function renders the counter value and a button. When the button is clicked, it increments the counter using swap!, and Reagent automatically re-renders the updated value.