A templating engine for Clojure
Add majavat to dependency list
[org.clojars.jj/majavat "1.12.6"](:require
[jj.majavat :as majavat]
[jj.majavat.renderer.sanitizer :refer [->Html]])
(def render-fn (majavat/build-renderer "index.html"))
(render-fn {:user "jj"})Additional options can be passed with
(def render-fn (majavat/build-renderer "index.html" {:cache? false
:renderer (->StringRenderer {:sanitizer (->Html)})}))
(render-fn {:user "jj"})All supported options:
| Option | Default Value | Supported Options |
|---|---|---|
renderer |
StringRenderer |
Any Renderer implementation |
cache? |
true |
true, false |
template-resolver |
ResourceResolver |
TemplateResolver |
Rendering file.txt with content
Hello {{ name }}!
(def render-fn (build-renderer "file.txt"))
(render-fn {:name "world"}) ;; => returns Hello world!or with a filter
Hello {{ name | upper-case }}!
(def render-fn (build-renderer "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 (build-renderer "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 (build-renderer "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 (build-renderer "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 (build-renderer "input-file"))
(render-fn {}) ;; returns "included foo"You can set value within a template via:
hello {% let foo = "baz" %}{{ foo }}{% endlet %}
(def render-fn (build-renderer "input-file"))
(render-fn {}) ;; returns "hello baz"or
hello {% let foo = bar %}{{ foo.baz }}{% endlet %}
(def render-fn (build-renderer "input-file"))
(render-fn {:bar {:baz "baz"}}) ;; returns "hello baz"file.txt content
foo
{% block %}
baz
Rendering input file with content:
{% extends "file.txt" %}
bar
(def render-fn (build-renderer "input-file"))
(render-fn {}) ;; returns "foo\nbar\nbaz"input-file with content
foo{# bar baz #}
(def render-fn (build-renderer "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 (build-renderer "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 (build-renderer "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 (build-renderer "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 (build-renderer "input-file"))
(render-fn {}) ;; returns "foo{{bar}}{%baz%}{#qux#}quux"Supported options:
:sanitizer - Input sanitization strategy
Renders a template using the provided context.
- template - template AST
- context - Map of variables for template interpolation
Returns - Rendered output
Returns rendered output as a String clojure
(->StringRenderer {:sanitizer (->Html)})Returns rendered output as an InputStream for streaming large content
(->InputStreamRenderer {:sanitizer (->Json)})The TemplateResolver protocol provides a uniform interface for accessing template content from different sources.
Returns reader for that template, or nil if not found.
(open-reader "/templates/header.html")Check if template exists at a path.
(template-exists? resolver "/templates/footer.html") ;; => true- ResourceResolver (default) - Reads from classpath
- FsResolver - Reads from filesystem
Sanitizer protocol provides a way to sanitize and cleanup values.
(sanitize (->Html) "<foo>bar</baz>") ;; => <foo>bar</baz>- Html - implementation for html pages
- Json - implementation for Json
- File Renderer - Renders output directly to file.
- TTL Builder - Reloads cache on a scheduled interval.
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.