Html
Html module: HTML escaping, safe HTML helpers, and template composition.
Provides utilities for escaping HTML entities, marking strings as safe (pre-escaped) HTML, rendering lists of items into IOLists, and composing template partials and layouts.
IOList is the canonical "rendered HTML fragment" type throughout this module. Fragments can be stored in Vault for fragment caching and re-embedded into parent templates via ~H sigils without double-escaping.
Types
Functions
Compute a content hash over an IOList for ETag generation.
Html.content_hash(~H"<p>Hello</p>") -- a hex string, e.g. "a1b2c3d4e5f6..."
Delegates to `IOList.hash` (FNV-1a 64-bit over segments, no flattening).
The returned hex string is suitable for use as an HTTP ETag value or as a
Vault cache key for fragment caching.
Cost: O(total bytes across all segments).Escape HTML entities in a string: & < > " '
Html.escape("a & b") -- "a & b"
Html.escape("<script>") -- "<script>"
Html.escape("he said \"hi\"") -- "he said "hi""
Replaces & first to avoid double-escaping, then < > " '.
Cost: O(n) per replacement pass.Escape a string for use in an HTML attribute value context.
Html.escape_attr("hello & world") -- "hello & world"
Html.escape_attr("<script>") -- "<script>"
Currently identical to `Html.escape`. Kept as a separate function so
attribute-specific escaping rules can be tightened in the future without
breaking call sites.Join a list of IOLists with a separator string.
Html.join([IOList.from_string("a"), IOList.from_string("b")], ", ")
-- IOList containing "a, b"
Cost: O(n) where n is the length of the list.Wrap content in a standard HTML document structure.
Html.layout("My Page", IOList.empty(), page_content)
Produces:
<!DOCTYPE html><html><head><title>My Page</title></head>
<body>...page_content...</body></html>
- `title` is HTML-escaped automatically.
- `head_extra` is an IOList inserted after `</title>` and before `</head>`.
Use it for `<link>`, `<meta>`, and `<style>` tags.
- `body` is an IOList (result of ~H or render_partial) inserted inside `<body>`.
Typical use:
fn page(conn) do
Html.layout(
"Dashboard",
IOList.from_string("<link rel=\"stylesheet\" href=\"/app.css\">"),
body_content
)
end
Cost: O(1) for IOList construction; O(total bytes) when flushed to string.Render a list of items into an IOList using a template function.
Html.list(["a", "b"], fn item -> IOList.from_string("<li>" ++ item ++ "</li>"))
-- IOList containing "<li>a</li><li>b</li>"
Cost: O(n) where n is the length of the list.Mark a string as safe HTML (bypass escaping).
Html.raw("<b>bold</b>") -- Safe("<b>bold</b>")
Use this when you have pre-escaped HTML that should be inserted verbatim.Map a template partial over a list of items, returning a single IOList.
Html.render_collection(users, fn user -> IOList.from_string("<li>" ++ user.name ++ "</li>"))
-- IOList containing all rendered <li> items concatenated
Equivalent to `Html.list(items, partial_fn)`. The Rails-flavoured name
makes the intent clear when building list views.
The returned IOList can be embedded in a parent ~H sigil without
double-escaping or stored in Vault as a cached fragment.
Cost: O(n) where n is the length of the list.Call a template partial function with a single argument, returning IOList.
fn user_card(user) do IOList.from_string("<div>" ++ user.name ++ "</div>") end
Html.render_partial(fn user -> user_card(user), current_user)
Partials are just functions that return IOList. This helper makes the
intent explicit and ensures the return type is IOList so the result
can be safely embedded in a parent ~H sigil without double-escaping,
or stored in Vault as a cached fragment.
Cost: O(1) — just calls the function.Extract the string from a Safe value.
Html.to_string(Html.raw("<b>bold</b>")) -- "<b>bold</b>"Build an HTML tag with properly escaped attributes and an IOList body.
Html.tag("div", [("class", "greeting")], IOList.from_string("Hi"))
-- "<div class=\"greeting\">Hi</div>"
Html.tag("br", [], IOList.empty())
-- "<br></br>" (use self-closing syntax in static HTML if needed)
DEPRECATED — prefer a `~H` template. `~H` walks its literal chunks through the
HTML context automaton at compile time and escapes every interpolation for the
position it actually lands in, with unsafe positions reported as compile
errors. `Html.tag` builds markup outside the sigil, so none of that analysis
applies and it must check at runtime instead.
Attribute VALUES are escaped for the context their attribute name implies:
a url attribute (`href`, `src`, `action`, …) gets the URL scheme allowlist,
`style` gets CSS declaration escaping, everything else gets attribute
escaping.
Element and attribute NAMES are validated, not escaped, and an invalid one
PANICS. Escaping cannot help there — `onerror` has no character to escape —
so a name built from untrusted input is a programming error, not a data
condition. Event-handler attributes (`on*`) are refused outright: their value
is JavaScript, and `Html.tag` has no way to know whether the caller meant
that.
The content is an IOList so it can be a composed partial or ~H result.
Passing the result of a ~H sigil as content is safe: it will not be
double-escaped.
Cost: O(k) for attribute serialisation where k = number of attributes,
plus O(1) for IOList wrapping.Trust a string as an ordinary attribute value. See trust_html.
Trust a string as CSS. See trust_html.
Trust a string as HTML element content.
Inserted verbatim where the surrounding template is element content, and
escaped anywhere else — trusting a string as HTML says nothing about whether
it is a safe URL, so the same value in an `href` still gets the scheme
allowlist.
Prefer building markup with the ~H sigil, which needs no trust at all. Reach
for this only when a string genuinely is markup from a source you control.Trust a string as JavaScript. See trust_html.
Trust a string as a URL. It bypasses the scheme allowlist, so only use it on a URL you constructed yourself. See trust_html.
Unwrap a trusted attribute value back to a plain String.
Unwrap trusted CSS back to a plain String.
Unwrap trusted HTML back to a plain String.
Unwrap a trusted URL back to a plain String.