zt Brings Type-Safe HTML Templates to Zig, Checked at Build Time
The zt library turns HTML templates into Zig code at build time, letting the compiler check types and parameters. Dynamic values are escaped by default, but the project is not yet mature.
In an article published on October 12, 2026, the developer behind zio and Dusty introduced zt, an HTML templating library for Zig. As the author explains in “Type-safe HTML templating in Zig,” templates are converted into Zig code during zig build: the compiler checks parameters, and dynamic values are escaped by default. A few people are already using the library, but the author does not yet consider the project mature.
How zt templates work
A zt template looks like HTML with Zig embedded in it, rather than a series of calls that assemble markup. You can declare typed parameters, bring in a model from application code, display its fields, and use ordinary if, for, and switch statements. For example, a page can accept an optional user and show different sections to a signed-in visitor and a guest.
At build time, zt parses the template and generates Zig code. An application can import the resulting template and render it, while the template can import application types. That means a misspelled field name or an argument of the wrong type is caught at compile time, not after someone opens the page. The compiler error points to the generated code; comments in that code help locate the corresponding place in the source template.
Rendered output is written directly to std.Io.Writer. It can be sent to a socket or collected in a buffer, with no intermediate HTML representation required. In Dusty, the call that renders an HTML response also hides the details of working with the writer. The author separately cautions that his simple example of writing to standard output does not handle errors related to cancelled std.Io operations correctly in every situation.
For repeated markup, zt provides components that can be passed around as values. Layouts can wrap nested templates using @children, providing a way to build a shared page structure without a separate template inheritance mechanism. Page code stays alongside familiar HTML markup, while its inputs remain explicit rather than becoming implicit variables.
Security and open questions
Dynamic values are escaped by default in both text and attributes. URL attributes, including href and src, are checked for dangerous schemes. Unescaped HTML output is possible, but must be explicitly marked with {!value}. The parser also requires elements to be closed, so templates cannot contain unfinished markup.
Escaping and URL scheme checks help prevent common causes of XSS, though they do not protect against every possible mistake. Requiring closed elements serves a different purpose: it helps prevent invalid markup. The option to insert raw HTML remains; whether to use it is up to the developer.
One part of the syntax is still under discussion. If a line starts with if, for, or switch, the parser treats it as code. As a result, ordinary text beginning with one of those words can cause a parse error: it has to be placed inside an element or written as a string expression. The author is considering explicit @if, @for, and @switch syntax, but the article does not announce a final decision. Nor does it give a timeline for a more mature release.
What this means for web projects
For a developer already building a backend in Zig, zt addresses a practical problem: how to serve HTML without assembling it through string concatenation or giving up type checking. The author demonstrates its use in Dusty and mentions a TODO app built with htmx, where templates work alongside other parts of his web stack.
For a site owner or marketer, this is not a reason to move a project to Zig. The benefit is narrower: if a team has already chosen that stack, it can catch template data errors at build time without adding escaping by hand for every value. At the same time, the library’s early stage and the unresolved syntax question are worth considering before relying on it across a large collection of pages.
My take
I like the direction: instead of adding another layer between data and the page, zt uses the Zig compiler and writes the result directly to a writer. The combination of typed parameters and escaping by default is particularly valuable. But I would assess the tool against a specific task and check how easy the templates are to maintain after code generation, especially when compiler errors need investigating.
For a typical landing page, I would not choose a new stack just for a templating library: server-side rendering or a static site is often easier to maintain. But for a team already writing a web app in Zig, zt is worth watching. It tackles a real problem—producing HTML without assembling strings by hand—even if the project still leaves some questions open.
Sources
Where the news comes from. The text is a retelling in the author’s own words; the facts come from the source, the opinion is the author’s.
- Type-safe HTML templating in Zig lalinsky.com
full-stack developer
I design web services and stores, and spend my free time experimenting with queues, caching, and build tools. I like it when a tool solves a real problem instead of adding another layer of abstraction.
All posts by the authorRelated articles
Chrome 157 Brings HTML-in-Canvas to Interactive DOM Content in Graphics Scenes
Chrome 157 has shipped HTML-in-Canvas in its stable release. The API lets developers embed interactive HTML in graphics scenes while keeping text, forms, and other DOM elements accessible.
Proa Unveiled: A Rust Framework for Server-Side Rendering and Multiple Output Formats
Proa debuted on Hacker News as a Rust web framework for server-side rendering and components that output HTML and Markdown. Its creators suggest trying it on one route of an existing app.
Bez Generates a Browser Engine from Web Specifications and Tests
Bez builds a browser engine from web specifications, checking its results against Chromium, Firefox, and WebKit. The project is still at an early stage, and its published figures do not mean the engine is ready.
Discussion 4
Vazir S.
The build-time type checks and default escaping sound useful, especially for catching mistakes before a page runs. Since zt is still early, how are you thinking about template errors—does the compiler point back to the original template line, or mainly to the generated Zig code?
Eteri Tevdoradze Post author
That’s one of the open questions I’m watching too. The build step gives zt a chance to report template locations, but I haven’t seen enough detail to claim errors consistently point to the original line rather than generated Zig; I’d want to try a few malformed expressions and missing-parameter cases before relying on that in a real project. Clear source mapping will matter a lot for day-to-day debugging.
Urho Vainio
Agreed that source mapping is a practical requirement, not a polish item: a type error in a template is only useful if the developer can find the expression that caused it. I’d test both malformed expressions and missing parameters, then check whether diagnostics retain the template filename and line through the generated Zig step; otherwise the safety benefit may come with a frustrating debug loop.
Katya M.
@urho_vainio, otherwise the compiler finds the bug and sends us on a treasure hunt through generated Zig 😅