Hiccup Tips

Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.

Summary: Hiccup is a Clojure DSL for generating HTML. If you're using it, you might like these tips.

Hiccup is a Clojure Domain Specific Language (DSL) for programmatically generating HTML. It's one of the three tools I recommend in my Clojure Web stack. It's a thin layer over HTML, but oh, how I welcome that layer! The biggest win is that since it's an internal DSL, you can begin abstracting with functions in a way that you will never be able to, even in templating engines.

Hiccup is an example of a great Clojure DSL. It uses literal data structures pretty well, it's as or more readable than what it translates into, and, as a layer of indirection, solves a few sticky problems of HTML. If you don't know it, go check out the Clojure Cookbook recipe for Hiccup. Hiccup might take some getting used to, but once you do, you'll appreciate it.

This article will assume you are familiar with the syntax and want to up your game.

Cross-site Scripting Vulnerability

Ok, this one is a pretty bad problem. Hiccup generates Strings, just plain old Java strings. If you put a String in the body of a Hiccup tag, it will just concatenate it right in there, no questions asked. That's just asking to be exploited, because that String is going to be shipped off to the browser and rendered, scripts and all.

Most templating libraries will default to escaping any String you pass them. The non-default, usually obviously marked alternative is to pass in a String unescaped. Hiccup got this backwards and it just sucks. It means you have to do extra work to be secure, and if you forget just once, your site is vulnerable.

The fix: This is the work you have to do every time you're getting a String that could be from the "outside" (a form submission, API request, etc.). Normally, you'd do this:

[:div content-of-div]

That will work but it's unsafe. You should do this:

[:div (h content-of-div)]

That little h (hiccup.core/h to be exact) there means escape the String. It sucks, but that's how you do it in Hiccup. One day I want to write a secure fork of Hiccup.

Overloading vectors

One downside to Hiccup and any DSL that overloads the meaning of vectors is that vectors are no longer useful as sequences within Hiccup. They now mean "start a new HTML tag". It's not a huge deal, but I've spent a lot of time debugging my code, only to realize that I was using a vector to represent a sequence. I use literal vectors everywhere else (because they're convenient and readable), but in Hiccup land they're wrong.

The fix: You can't use a literal vector, but you can call list to create a list. Not as beautiful, but it is correct. Sometimes I will call seq on a return value to ensure that it's never a vector.

Multiple tags

I don't know why this still happens, but it's common, so I'll mention it. Sometimes I'll be looking at the HTML output in a browser and I just can't find an element. It's gone. Reload, still not there. When I look through the code, the hiccup to generate the tag is right there! Why won't it render?

Well, long story short, it's because in Clojure, only the last value of a let or fn is returned. Doh! My missing element was being rendered then discarded.

(defn list-with-header [header items]
  [:h3 header] ;; this header is missing
  [:ul
    (for [i items]
      [:li i])])

The fix: Wrap the two (or more) things in a list (not a vector!).

(defn list-with-header [header items]
  (list ;; wrap in a list, not a vector
    [:h3 header] ;; now it's there
    [:ul
      (for [i items]
        [:li i])]))

Hiccup plays nice with nil

This one is just a little design touch with some perks, not a problem that it's solving. In Hiccup, the empty list renders nothing. This is extended to nil as well. A common thing in HTML is that you want to render a bunch of children in a parent tag, but you don't want the parent tag if the list is empty.

Standard constructs will render the parent tag:

[:ul
  (for [i items]
    [:li i])]

When items is empty, you still get <ul></ul>. This is a problem with lots of templating libraries.

The fix: The fix in Hiccup is due to it playing nice with nil. Wrap the parent in a when:

(when (seq items)
  [:ul
    (for [i items]
      [:li i])])

It's not beautiful, but then again, you could be using HTML with Moustache.

Use defn for snippet abstraction

HTML has no way to abstract. The crap you see is the crap you get. Many templating libraries have some kind of snippet concept, often referring to different files. Well, because Hiccup is just code inside of Clojure, you've got a better designed way of making reusable pieces of Hiccup.

Let's say I'm repeating something a lot:

[:div
  [:ul
    (for [i list1]
      [:li i])]
  [:ul
    (for [i list2]
      [:li i])]
  [:ul
    (for [i list3]
      [:li i])]]

That ul is basically the same three times.

The fix: Standard functional abstraction. Pull out the repeated bit into a new, named function.

(defn make-list [items]
  [:ul
    (for [i items]
      [:li i])])

Now you can call it from inside of Hiccup:

[:div
  (make-list list1)
  (make-list list2)
  (make-list list3)]

Compiling your snippets

You may know this, but Hiccup is a compiling macro. That means it takes your literal vectors, maps, etc, and at compile time, generates code that will generate your HTML. All this means is that Hiccup is really fast, about as fast as concatenating strings can be.

But, because the Hiccup compiler doesn't do a full examination of your code, it can't compile everything. It inserts run time fallbacks for stuff it can't handle at compile time which will interpret it at run time. So, for instance, if you're calling a function that returns some Hiccup, it can't compile that automatically. It has to wait till the function returns to know what it is. That is, unless . . .

The fix: The way to get Hiccup to compile something is with the hiccup.core/html macro. That's the macro that does the compilation and it will do it anywhere. So if you've got code like this:

(defn make-list [items]
  [:ul
    (for [i items]
      [:li i])])

(defn my-three-lists []
  [:div
    (make-list list1)
    (make-list list2)
    (make-list list3)])

You should wrap the Hiccup form in its own compiler, like this:

(defn make-list [items]
  (html
    [:ul
      (for [i items]
        [:li i])]))

For this little example, it probably won't make a noticeable difference. But it can be significant for larger documents.

Just to note: the Hiccup compiler can understand if and for forms, so there's no need to wrap them in the compiler. No harm, though, if you do.

Autoterminating

Just a good thing to know about HTML.

Did you know that this is not legal HTML?

<script />

It's true. A script tag can't be self-closing.

There's all sort of silly rules in HTML like this. And then there's XML mode versus HTML mode. We are lucky: Hiccup does all of this for you, so you don't have to wade through the HTML spec(s!) looking for why something won't render in IE7.

The fix: hiccup.core/html takes a map of options as the first argument (it's optional). If you pass in a :mode option, it will set the correct HTML mode. The various modes unfortunately are incompatible. There are three modes, :xml, :html, and :xhtml. The default is :xhtml.

Id/class DSL

Hiccup is a DSL. And it has its own sub DSL for HTML ids and classes. It's similar to CSS selectors.

Let's say you have a div like this:

[:div
  {:id "main-content"
   :class "blue-background green-border"}
  (h "Here's some content.")]

Well, Hiccup lets you make that shorter and easier to read.

The fix: Use the id/class DSL:

[:div#main-content.blue-background.green-border
  (h "Here's some content")]

Here's how it works. Every element has an optional id and zero or more classes. After the tag name (div here), you can put the id after a #. Then list your classes starting with a . each. Omit the # if there's no id. Ditto for the . if there's no class. Oh, and the id must come first. That will get converted to the id and class attributes you want. Also, these will conflict with attributes in the map, so choose one or the other, not both.

Generating Hiccup from HTML

Last thing, I promise!! Sometimes you have some HTML that someone gave you and you want to Hiccupify it so you can just stick it into your code. Manually doing that is kind of tedious. Luckily, there's a solution our there for you.

The fix: This page lists some options for outputting HTML from Hiccup. I have personally used Hiccup-bridge. It does a good job (and it even goes both ways). You call it like this:

lein hicv 2clj hello.html

That will output hicv/hello.clj in hiccup format. Pretty rocking when you need it.

Conclusion

Well, there you go. My Hiccup tips. Hiccup is pretty nice for templating. I recommend using it (or my future secure fork) in your web projects. If you'd like to learn more Hiccup and how to build web apps in Clojure, please check out Lispcast Web Development in Clojure. It's a video course teaching just that. All you need to know is Clojure and HTML.