niccup

Hiccup-style HTML generation for Nix. Describe HTML as Nix lists/attrsets, render to strings. Heavily inspired by hiccup (Fast library for rendering HTML in Clojure) by @weavejester

View on GitHub

Installation

Flakes:

inputs.niccup.url = "github:embedding-shapes/niccup";
# use inputs.niccup.lib

Non-flakes:

let
  niccup = builtins.fetchTarball "https://github.com/embedding-shapes/niccup/archive/master.tar.gz";
  h = import (niccup + "/src/lib.nix") { };
in h.render [ "p" "Hello" ]

Security Note

This library assumes trusted inputs. Like server-side templates, the expressions should be developer-authored code, not user-generated content. Do not pass untrusted input directly to raw or comment, or do so at your own risk. Don't say you weren't warned tho.

Examples

let h = inputs.niccup.lib; in
h.render [
  "div#main.container"
  { lang = "en"; class = [ "app" "dark" ]; }
  [ "h1" "Hello from Nix" ]
  [ "p" "Hiccup-style HTML in Nix." ]
  (h.comment "List example")
  [ "ul" (map (x: [ "li.item" x ]) [ "one" "two" "three" ]) ]
]

Write to file (use nixpkgs writeText):

{ pkgs, inputs, ... }:
pkgs.writeText "index.html" (inputs.niccup.lib.render [ "p" "Hello" ])

Some more involved examples:

The website for niccup is generated dynamically with niccup too, the whole source is ~120 lines of Nix as well

Data Model

Element: [ tag-spec attrs? children... ]

Tag spec: string with optional CSS shorthand: "div#id.class1.class2". ID must precede classes.

Attributes (optional attrset, second position):

Children:

Void tags (img, br, hr, input, meta, link, area, base, col, embed, source, track, wbr): no closing tag.

API

Exported as lib from the flake.

Development

just build # checks syntax, builds the library

just test # runs a bunch of tests

just build-website # builds the project website to result/

just all-examples # builds all of the examples, currently `blog`, `quine` and `art`

just example blog # builds only the `blog` example

License

MIT 2025 - @embedding-shapes