back to blog posts

Hiccup Makes Writing HTML Fun

At $work, I’ve been writing a web application that is server-side rendered via Go’s html/template package.

I’ve been writing a toy application in Clojure with a similar approach: server rendered HTML with Tailwind for styling and AlpineJS for a hint of interactivity.

With the introduction of Go’s new embed package in 1.16, I was tempted to port the application to Go. I was imagining being able to embed the templates and assets in a static binary.

And I could still do that! But I hesitated as soon as I reached the portion of rewriting my templates in Go, and for good reason: Go’s template syntax isn’t great, and Clojure has Hiccup.

Hiccup #

Hiccup is a library for writing HTML in Clojure. Writing HTML in Hiccup is a dream:

And, more than anything, Hiccup is written within your regular Clojure functions, which makes it incredibly easy to interweave logic with your templates.

A <form> in Hiccup might look like this:

[:form {:action "/add-address"
        :method "POST"}
  [:label {:for "label-input"} "Label"]
  [:input {:type "text"
           :id "label-input"
           :name "label"
           ;; for now, label is required
           :required true}]

  [:label {:for "street-input"} "Street"]
  [:input {:type "text"
           :id "street-input"
           :name "street"}]

  [:label {:for "city-input"} "City"]
  [:input {:type "text"
           :id "city-input"
           :name "city"}]

  [:label {:for "state-input"} "State"]
  [:input {:type "text"
           :id "state-input"
           :name "state"}]]
;; …

The beauty begins when you start to weave in the logic:

(def disabled-classes "text-gray-500 …")

(defn button
  [id classes disabled?]
  [:button {:id id
            :class (if disabled?
                     (merge classes disabled-classes)
                     classes)
            :disabled disabled?}])

In this way, Hiccup is similar to the JSX syntax while writing React — and this approach has been taken to the ClojureScript side via the Reagent library. (A more thorough example of Reagent-style Hiccup code can be found in my seven-guis project).

And, when you pair Hiccup with Parinfer + Conjure, the DX is too good to pass up. While I love writing Go, the conciseness, simplicity and the REPL of Clojure have drawn me in. It’s magical.

🪄