README.org

May 11, 2026 ยท View on GitHub

#+title: Readme

HTML generator for Common Lisp

  • Purpose

Spinneret is great but having to use macros to compose elements is less intuitive than simply having a function to render S-Expressions into HTML.

Hiccl also aims to be easy to extend with generic functions.

  • Usage

Hiccl exposes one macro, render, which wraps render-forms to use &body args for editor support. It takes an output argument (same as format) and any number of SXML expressions.

  • Symbols are raw (you can write literal HTML between | symbols)
  • Strings are sanitized
  • Lists are HTML nodes

Outputting to nil returns a string.

NOTE: In the following examples, the HTML has been formatted for the readme. Hiccl does not pretty print it's output.

#+begin_src lisp (hiccl:render nil `(:div :hi "world" (:span "tag can be symbol") "no </"xss"> allowed" |symbols "raw" | (:a :href "hTtPs://link.org" "clickme"))) #+end_src

#+begin_src html

tag can be symbol no </"xss"> allowed symbols "raw" clickme
#+end_src

** JSX style syntax

Class and ID tags can use the following shorthand notation:

#+begin_src lisp :exports both (hiccl:render nil '(:div.c1#id1.c2 :class "c3" :id "id2")) #+end_src

#+begin_src html

#+end_src

Order is not guaranteed at the moment, but I will consider adding it.

** Specifying output

Render to a stream by specifying the out argument, renders to string if nil.

#+begin_src lisp (hiccl:render standard-output sxml) ;; alternatively you can use t like in format (hiccl:render t sxml) ;; to get a string (hiccl:render nil sxml) #+end_src

** Composing components

#+begin_src lisp (defun hello-component (name) `(:span ,(format nil "Hello ~a" name)))

(hiccl:render nil `(:div "hello-component:" ,(hello-component "garlic"))) #+end_src

#+begin_src html

hello-component: Hello garlic
#+end_src

** Boolean attributes

To use boolean attributes, just make its value nil:

#+begin_src lisp (hiccl:render nil '(:div :bool nil)) #+end_src

#+begin_src html

#+end_src

** Extending the renderer

The renderer uses generic functions that you can extend to handle your objects:

#+begin_src lisp :exports both (defclass thing () ((a :initarg :a :accessor thing-a) (b :initarg :b :accessor thing-b)))

(defmethod hiccl::render-form (out (obj thing)) (hiccl:render out `(:div.thing (:div.a ,(thing-a obj)) (:div.b ,(thing-b obj)))))

(hiccl:render nil (make-instance 'thing :a "hi" :b "world")) #+end_src

#+begin_src html

hi
world
#+end_src

You can also add special cases for HTML tags with methods:

#+begin_src lisp :exports both (defmethod hiccl::apply-tag (out (tag (eql :my-comment)) body) (format out "" body))

(hiccl:render nil '(:my-comment "hi")) #+end_src

#+begin_src html

#+end_src

These are not exported, so remember to use hiccl::* to refer to them.