README.org

January 20, 2026 · View on GitHub

#+title: html: Simple HTML generation

=html= is a simple library for generating HTML in the spirit of [[https://github.com/ruricolist/spinneret][Spinneret]] and [[https://github.com/weavejester/hiccup][Hiccup]]. =html= has no dependencies.

  • Table of Contents :TOC_5_gh:noexport:
  • [[#compatibility][Compatibility]]
  • [[#usage][Usage]]
    • [[#static-html][Static HTML]]
    • [[#dynamic-content][Dynamic Content]]
  • [[#see-also][See Also]]
  • Compatibility

| Compiler | Compiles? | |-----------+-----------| | SBCL | ✅ | | ECL | ✅ | | Clasp | ✅ | | ABCL | ✅ | | CCL | ✅ | | Clisp | ✅ | |-----------+-----------| | Allegro | ✅ | | LispWorks | ❓ |

  • Usage

With a focus on simplicity, =html= provides the bare necessities for generating HTML. Seekers of any higher sophistication should try [[https://github.com/ruricolist/spinneret][Spinneret]].

In the examples below, (in-package :html) is used for brevity, and it is assumed that in your own code you will prefix all calls with html:.

** Static HTML

For HTML with predetermined content, use the =html= macro:

#+begin_src lisp :exports both (in-package :html) (macroexpand (html (:raw "") (:html (:head (:meta :charset "utf-8")) (:body (:div :id "foo" :class "bar" (:span "hello")))))) #+end_src

#+RESULTS: #+begin_example

hello
#+end_example

Here we see examples of:

** Dynamic Content

Naturally you'll also want to dynamically inject content into certain elements. The expansion logic used within the =html= macro is a standalone function named =expand= that you can also call yourself, provided you have a =stream= on hand.

#+begin_src lisp :exports both (in-package :html) (with-output-to-string (stream) (let ((name "Jack")) (expand stream `(:span ,name)))) #+end_src

#+RESULTS: : : Jack :

  • See Also