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
#+begin_src html
#+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
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
** 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
** 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
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.