This is an experimental project. Do not use for production level.
A small Clojure library for generating HTML using plain Clojure expressions. It is intended for static site or page generation, and is not suitable as a dynamic web page renderer.
Java and Clojure must be installed.
Clojure installation guide: https://clojure.org/guides/install_clojure
A separate CLI tool, juce-cli, is also available.
https://github.com/tmkw/juce-cli
(div {:class "greeting"} (span "Hello, world!"))Output:
<div class="greeting"><span>Hello, world!</span></div>The notation { .... } represents an attribute map.
You cannot omit quotation marks as shown below, because juce follows standard Clojure syntax.
(div {:class greeting} (span Hello, world!))
;; => errorjuce doesn't provide DSL expression for HTML comment. If you want to include an HTML comment, write it directly as a string:
(html
(div
"<!--
This is a comment.
-->"
(button "Push Me")))Attributes can be written directly after the tag name using keyword/value pairs.
(div :class "greeting" :id 123 "Hello")The example above is equivalent to:
(div {:class "greeting" :id 123} "Hello")Attribute shorthand works only while arguments appear as keyword/value pairs.
(div
(for [name ["foo" "bar"]]
(span (str "Hello, " name))))Output:
<div><span>Hello, foo</span><span>Hello, bar</span></div>Add juce to your deps.edn:
{:deps {io.github.tmkw/juce {:mvn/version "0.2.5"}}}
juce provides helper functions for building HTML, so it works as a DSL as well as regular Clojure code.
(require '[juce.core :refer [div span]])
(div {:class "greeting"} (span "Hello, world!"))
;;=> <div class="greeting"><span>Hello, world!</span></div>(require '[juce.core :as j])
(j/render "(div {:id \"x\"} (p \"yey!\"))")
;;=> <div id="x"><p>yey!</p></div>(j/render-file "path/to/file")(j/render "(section (p (:name hoge)))" {:hoge {:name "HOGE"}})
;;=> <section><p>HOGE</p></section>juce’s built‑in tags (div, span, p, …) are ordinary Clojure functions that return HTML strings. You can define your own tags the same way.
-
Define a Clojure function whose name represents the tag or component you want to create. Examples:
button,card,hero-section, etc. -
The function may take any number or shape of arguments. juce does not enforce the interface of custom tags.
-
The function must return a valid HTML string. This is the only requirement. juce’s rendering pipeline expects HTML as the final output.
-
You may freely use juce’s tag functions inside your custom tag, or you can also generate HTML manually.
(ns my.custom.tags
(:require [juce.core :as j]))
(defn button [label]
(j/div :class "my-button"
(j/span label)))You can also implement custom tags without using juce at all. The following example is equivalent to the above one.
(ns my.custom.tags)
(defn button [label]
(str "<div class=\"my-button\"><span>" label "</span></div>"))Both versions work as expected:
(require '[my.custom.tags :as m] '[juce.core :as j :refer [section]])
(m/button "hey!")
;; => <div class="my-button"><span>hey!</span></div>
(section (m/button "Hi!"))
;; => <section><div class="my-button"><span>Hi!</span></div></section>Custom tags can also be used inside render and render-file.
(require '[juce.core :as j])
(j/ns-binding '[my.custom.tags :as m]
(j/render "(m/button \"hey!\")"))
;; => <div class="my-button"><span>hey!</span></div>ns-binding, a macro that temporarily loads external namespaces into juce.core so that custom tag functions can be used inside render.
(require '[juce.core :as j])
(j/ns-binding '[my.custom.tags :as t]
(j/render "(div (t/button \"Click me\"))"))ns-binding temporarily loads the namespace my.custom.tags into juce.core with alias t,
so aliases such as t/button can be resolved inside render.
Output is compact. Use an external formatter if needed.
doseq returns nil, so it produces no HTML. Use for instead.
(require '[juce.core :as j])
(j/render "(div (doseq [n [1 2 3]] (p (str n))))")
;;=> returns <div></div>, not <div><p>1</p><p>2</p><p>3</p></div>
;; because doseq returns nil.
(j/render "(div (for [n [1 2 3]] (p n)))")
;;=> returns <div><p>1</p><p>2</p><p>3</p></div>Use them from the juce.util namespace.
- meta
- time
- source
- map
It is recommended to use them with a namespace qualifier to avoid name collisions.
(require '[juce.core :as j :refer [div p]]
'[juce.util :as u])
(div
(p "abcde")
(u/time {:datetime "2026-01-02"} "1 Jan 2026"))juce evaluates Clojure expressions directly. Use it with trusted, developer-authored templates such as static site content. It is not intended for rendering untrusted input in web applications.
juce is distributed under the BSD 2-Clause License (SPDX: BSD-2-Clause). See the LICENSE file for details.
Takanobu Maekawa