# Templating
Deft's server-side templating is built on **block literals** —
`[html ... \html]`, `[sql ... \sql]`, `[codeblock ... \codeblock]`.
A block is a multi-line literal whose interior is tokenized: literal
text, `$var` interpolation, and nested `[...]` calls all mix freely.
The compiler packages the interior as a tagged map and calls the
block-named function, which applies domain-appropriate escaping and
returns the rendered result.
## How blocks work
`[name ... \name]` desugars to a single ordinary call:
[name %{ :parts @[ part1 part2 ... ] }]
- The block name resolves through the normal namespace chain — it is
just a function. The `html` function comes from
`from "deft/stdext/html" import @{ html html-raw }`; you can define your
own block handlers the same way.
- `:parts` is a vector of already-evaluated parts:
| Part | Shape | Meaning |
|---|---|---|
| string | `"text"` | Literal block text — emit unchanged |
| `$var` | `%__escape{:value ...}` | Interpolated value — apply domain escaping |
| trusted | `%__HtmlSafe{...}` / `%__SqlSafe{...}` | Pre-escaped — pass through |
| list | `@[ ... ]` | Collected results (e.g. `for` loop bodies) — recurse and join |
The handler's job: walk the parts, escape each per its domain, and
join. That is the entire contract — see
[Defining your own templates](#defining-your-own-templates).
## `[html ... \html]`
The workhorse. Returns a `%__HtmlSafe{:value ...}` tagged map (a plain
string that knows it is safe for embedding).
from "deft/stdext/html" import @{ html html-raw html-escape } def render-user {| user^map | [html <div class="user"> <h1>$user~name</h1> <p>Age: $user~age</p> </div> \html] } def handler {|req res| http/html $res [render-user $alice] }
### Interpolation forms
Any form from string interpolation works inside a block:
| Form | Example | Notes |
|---|---|---|
| `$var` | `$name` | Variable or `$user~field` chain |
| `$var(args)` | `$xs(len)` | Postfix call |
| `$(expr)` | `$($price * $quantity)` | Infix expression |
| `$[cmd]` | `$[upper $name]` | Prefix call — result used verbatim |
| `[cmd]` | `[upper $name]` | Bare bracket call also interpolates |
Escape a literal `[` as `\[`.
### Escaping is explicit
A block is a **raw** template: interpolated `$var` values pass through
**unescaped**. Escape dynamic text explicitly:
- `[html-escape $text]` — entity-escape (`&` `<` `>` `"` `'`) for safe
text interpolation.
- `[html-raw $markup]` — mark a string as trusted HTML (no escaping).
set bio "<b>bold</b>" [html <p>[html-escape $bio]</p> # <b>bold</b> <p>[html-raw $bio]</p> # <b>bold</b> \html]
Anything wrapped in `__HtmlSafe` (the result of `[html ... \html]`
or `[html-raw ...]`) passes through nested blocks unchanged — that is
how partials compose.
### Partials and nesting
A function that returns `[html ... \html]` can be embedded directly in
an outer block:
def render-user {| user^map | [html <h1>$user~name</h1> \html] } [html <div class="card"> [render-user $user] </div> \html]
### Loops
A bracketed `[for ...]` inside a block collects each iteration's body
results into a list part, which the handler joins. Emit a nested
`[html ... \html]` block per item:
def user-list {| users^list | [html <ul> [for u $users { [html <li>[html-escape $u~name]</li> \html] }] </ul> \html] }
### Raw blocks
`[*name ... \name]` captures the interior **verbatim** — no `$var` or
`[cmd]` interpolation at all. Useful for source listings:
[*codeblock
set x $who
[str $x]
\codeblock] # $who stays literal; the block is syntax-highlighted
## Defining your own templates
Any function of one argument can be a block handler. The block map
arrives as `%{ :parts @[...] }` tagged with the block name; unwrap each
part by type and apply your own escaping rules.
def block-part-str {| part | if ([type $part] == "__escape") { # $var interpolation — apply your own escaping here [str [get $part :value]] } else if ([type $part] == "__HtmlSafe") or ([type $part] == "__SqlSafe") { # pre-escaped content — trust it [str [get $part :value]] } else if [is? "list" $part] { # collected loop bodies — recurse and join [join [collect [map $part {[block-part-str $it]}]] ""] } else { # literal text [str $part] } } def my-pill {| block^map | set body [join [collect [map [get $block :parts] {[block-part-str $it]}]] ""] [html <span class="pill">$body</span> \html] } [my-pill hi $who \my-pill] # → <span class="pill">hi world</span>
Return an `__HtmlSafe` value (via `[html ... \html]` or
`[html-raw ...]`) so your template nests safely inside other blocks.
The stdext package's `html.dft` and `sql.dft` are the reference
implementations:
- `packages/stdext/src/html.dft` — `html`, `html-raw`, `html-escape`
- `packages/stdext/src/sql.dft` — `sql`, `sql-raw`, `sql-escape-value`
## `[sql ... \sql]` — another example
The same block machinery, with SQL-aware escaping. Where `html` is
string-based, `sql` escapes values by their Deft type:
| Value | SQL literal |
|---|---|
| string | `'O''Brien'` (single-quoted, quotes doubled) |
| number | `42`, `3.14` |
| boolean | `TRUE` / `FALSE` |
| nil | `NULL` |
| list | `(1, 2, 'text')` |
| map | `"col" = 'val' AND ...` (WHERE-style) |
from "deft/stdext/sql" import @{ sql sql-raw } set name "O'Brien" set id 42 set active true set query [sql SELECT * FROM users WHERE name = $name AND id = $id AND active = $active \sql] # SELECT * FROM users # WHERE name = 'O''Brien' AND id = 42 AND active = TRUE
Use `[sql-raw ...]` for trusted fragments the type-aware escaper
would mangle — function calls, identifiers, operators:
[sql SELECT * FROM users WHERE updated_at > [sql-raw "NOW() - INTERVAL '1 day'"] \sql]
Pairs naturally with `sqlite/query`:
set rows [sqlite/query $db [sql SELECT * FROM users WHERE name = $name \sql]]
## Quick Reference
| Form | Purpose |
|---|---|
| `[html ... \html]` | Render an HTML fragment; returns `__HtmlSafe` |
| `[html-raw $x]` | Mark a string as trusted HTML |
| `[html-escape $x]` | Entity-escape text for safe interpolation |
| `[sql ... \sql]` | Build a SQL string with type-aware value escaping |
| `[sql-raw $x]` | Mark a fragment as trusted SQL |
| `[*name ... \name]` | Any block, captured verbatim (no interpolation) |
| `[myname ... \myname]` | Your own block handler — any 1-arg function |