Newsletter
Join the Community
Subscribe to our newsletter for the latest news and updates
A template language for Roc with compile time validation and tag unions
Roc Template Language (RTL) is a developer tool for the Roc programming language that turns .rtl template files into a generated Pages.roc module, where each template becomes a type-checked Roc function. It takes a single record argument called model and returns rendered string output, giving you compile-time validation for HTML or any other textual content.
RTL is a template engine designed specifically for Roc, a functional programming language. The core idea is that you write templates containing literal text plus RTL tags for dynamic values, loops, conditionals, and pattern matching. Running the rtl command in a directory containing .rtl files generates Pages.roc, which exposes one Roc function per template. Each function accepts a model argument of any type (normally a record) and returns the fully rendered string. Because the generated code is ordinary Roc, the Roc compiler validates both template syntax and the types of all interpolated values. The project is authored by Isaac Vando and hosted on GitHub under the name rtl.
{{ model.name }} are escaped by default, preventing cross-site scripting (XSS) vulnerabilities in HTML output.{{{ model.dynamic_html }}} inserts unescaped content, which is useful for non-HTML content types or combining multiple rendered templates.{|list pattern : list_expr |} ... {|endlist|} block repeats content for each element, and the pattern can be any Roc pattern such as (x, y).{|if condition |} ... {|endif|} and optional {|else|} blocks support any Roc boolean expression, so comparisons and function calls work directly.{|when x |} ... {|is Ok(y) |} ... {|endwhen|} syntax, integrating with Roc's tag union types.{|import MyModule |} brings an arbitrary Roc module into scope for use inside template expressions.rtl with no arguments in a directory to process all .rtl files and output Pages.roc automatically.when expressions to branch on tag unions like Ok/Err, producing type-safe error handling directly in the template layer.Write a .rtl file mixing literal content with RTL tags, then run the rtl executable in that directory. The tool parses each template and generates Pages.roc, a normal Roc source file that defines one function per template. For example, a template named hello.rtl becomes a function Pages.hello that takes a model record and returns the rendered HTML string. The generated functions are fully type-checked by the Roc compiler, so any wrong field name, non-string interpolation, or malformed tag becomes a compile error.
.rtl files requires manually mapping *.rtl to HTML in editor settings; the project is early-stage, with whitespace control, tag escaping, and hot reloading still on the todo list.Running rtl in a directory containing .rtl templates produces a single file named Pages.roc. It exposes one Roc function for each template, and each function takes a model argument and returns the rendered string.
Yes. Values inserted with double curly brackets, like {{ model.name }}, are HTML-escaped before insertion. Use triple brackets ({{{ ... }}}) to insert raw, unescaped content.
Yes. RTL is designed for any textual content. Since normal interpolation escapes HTML, you can use raw interpolation to output JSON, XML, Markdown, or other formats without unwanted escaping.
Currently yes. The README provides build instructions using roc build rtl.roc --optimize and moving the resulting binary to /usr/local/bin. No prebuilt releases or package-manager installs are documented.
The repository's todo list includes adding control over whitespace around RTL tags, allowing tags to be escaped, exploring real hot code reloading, switching generated code to buffer-passing style for fewer copies, benchmarking runtime performance, and improving error messages for incomplete tags.