A templating engine for Clojure
Add majavat to dependency list
[org.clojars.jj/majavat "1.11.1"]
(:require
[jj.majavat :as majavat]
[jj.majavat.renderer.sanitizer :refer [->Html]])
(def render-fn (render "index.html"))
(render-fn "index.html" {:user "jj"})
Additional options can be passed with
(def render-fn (render "index.html" {:return-type :input-stream
:sanitizer (->Html)}))
(render-fn {:user "jj"})
All supported options:
Option | Default Value | Supported Options |
---|---|---|
return-type | :string | :string , :input-stream |
cache? | true | true , false |
template-resolver | ResourceResolver | Any TemplateResolver implementation |
sanitizer | nil | Any Sanitizer implementation |
Rendering file.txt
with content
Hello {{ name }}!
(def render-fn (render "file.txt"))
(render-fn {:name "world"}) ;; => returns Hello world!
or with a filter
Hello {{ name | upper-case }}!
(def render-fn (render "file.txt"))
(render-fn {:name "world"}) ;; => returns Hello WORLD!
filters | type | example |
---|---|---|
capitalize | String | "hello world" → "Hello world" |
lower-case | String | "HELLO WORLD" → "hello world" |
title-case | String | "hello world" → "Hello World" |
trim | String | " hello " → "hello" |
upper-case | String | "hello world" → "HELLO WORLD" |
upper-roman | String | "iv" → "IV" |
long | String | "123" → 123L |
int | String | "123" → 123 |
name | keyword | :name → "name" |
inc | Number | 5 → 6 |
dec | Number | 5 → 4 |
file-size | Number | 2048 → "2 KB" |
default "foo" | nil | nil -> "foo" |
date "yyyy" | LocalDate | Instance of LocalDate -> "2025" |
date "yyyy" | LocalDateTime | Instance of LocalDateTime -> "2025" |
date "hh/mm" | LocalTime | Instance of LocalTime -> "11/11" |
date "hh/mm" "Asia/Tokyo" | ZonedDateTime | Instance of ZonedDateTime -> "11/11" |
date "hh/mm" "Asia/Tokyo" | Instant | Instance of Instant -> "11/11" |
Rendering input file with content:
"Hello {% if name %}{{name}}{% else %}world{% endif %}!"
(def render-fn (render "input-file"))
(render-fn {:name "jj"}) ;; returns "Hello jj!"
(render-fn {}) ;; returns "Hello world!"
or
"Hello {% if not name %}world{% else %}jj{% endif %}!"
(def render-fn (render "input-file"))
(render-fn {:name "foo"}) ;; returns "Hello jj!"
(render-fn {}) ;; returns "Hello world!"
Rendering input file with content:
{% for item in items %}
- {{ item }} is {{ loop.index }} of {{ loop.total }}
{% endfor %}
(def render-fn (render "input-file"))
(render-fn {:items ["Apple" "Banana" "Orange"]}) ;; returns "- Apple is 0 of 3\n- Banana is 1 of 3\n- Orange is 2 of 3"
The loop context provides access to:
loop.total
- total number of items in the collection
loop.index
- current 0-based index position
loop.first?
- true only for the first item
loop.last?
- true only for the last item
file.txt content
foo
Rendering input file with content:
included {% include "file.txt" %}
(def render-fn (render "input-file"))
(render-fn {}) ;; returns "included foo"
You can set value within a template via:
hello {% let foo = "baz" %}{{ foo }}{% endlet %}
(def render-fn (render "input-file"))
(render-fn {}) ;; returns "hello baz"
or
hello {% let foo = bar %}{{ foo.baz }}{% endlet %}
(def render-fn (render "input-file"))
(render-fn {:bar {:baz "baz"}}) ;; returns "hello baz"
file.txt content
foo
{% block content %}
baz
Rendering input file with content:
{% extends content "file.txt" %}
bar
(def render-fn (render "input-file"))
(render-fn {}) ;; returns "foo\nbar\nbaz"
input-file with content
foo{# bar baz #}
(def render-fn (render "input-file"))
(render-fn {}) ;; returns "foo"
CSRF token can be added via
{% csrf-token %}
and when rendering file :csrf-token
has to be provided
(def render-fn (render "input-file"))
(render-fn {:csrf-token "foobarbaz"}) ;; returns <input type="hidden" name="csrf_token" value="foobarbaz">
input-file with content
/foo{% query-string foo %}
(def render-fn (render "input-file"))
(render-fn {:foo {:count 2}}) ;; returns "/foo?count=2"
input-file with content
default format {% now %}
formatted {% now "yyyy-MM-dd" %}
formatted with tz {% now "yyyy-MM-dd hh:mm " "Asia/Tokyo" %}
(def render-fn (render "input-file"))
(render-fn {}) ;; returns "default format 2011/11/11 11:11\nformatted 2011-11-11\ntormatted with tz 2011-11-11 23:11"
input-file with content
{% verbatim %}foo{{bar}}{%baz%}{#qux#}quux{% endverbatim %}
(def render-fn (render "input-file"))
(render-fn {}) ;; returns "foo{{bar}}{%baz%}{#qux#}quux"
The TemplateResolver
protocol provides a uniform interface for accessing template content from different sources.
open-reader
Returns reader for that template, or nil
if not found.
(open-reader "/templates/header.html")
template-exists?
Check if template exists at a path.
(template-exists? resolver "/templates/footer.html") ;; => true
Sanitizer
protocol provides a way to sanitize and cleanup values.
(sanitize (->Html) "<foo>bar</baz>") ;; => <foo>bar</baz>
Copyright © 2025 ruroru
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.
Can you improve this documentation? These fine people already did:
jj & ruroruEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
Ctrl+k | Jump to recent docs |
← | Move to previous article |
→ | Move to next article |
Ctrl+/ | Jump to the search field |