plait
A modern, type-safe HTML templating library for Rust that embraces composition.
Plait lets you write HTML directly in Rust using the html! macro, with compile-time validation, automatic
escaping, and a natural syntax that mirrors standard HTML and Rust control flow. Reusable components are defined
with the component! macro.
Quick start
use ;
let name = "World";
let page = html! ;
assert_eq!;
The html! macro returns an HtmlFragment that implements ToHtml. Call .to_html()(ToHtml::to_html) to
get an Html value (a String wrapper that implements Display(std::fmt::Display)).
Syntax reference
Elements
Write element names directly. Children go inside braces. Void elements (like br, img, input) use a semicolon
instead.
let frag = html! ;
Snake-case identifiers are automatically converted to kebab-case:
// Renders as <my-element>...</my-element>
let frag = html! ;
assert_eq!;
DOCTYPE
Use #doctype to emit <!DOCTYPE html>:
let page = html! ;
assert_eq!;
Text and expressions
String literals are rendered as static text (HTML-escaped). Rust expressions inside parentheses are also
HTML-escaped by default. Use #(expr) for raw (unescaped) output.
let user = "<script>alert('xss')</script>";
let frag = html! ;
Expressions in () must implement RenderEscaped. Expressions in #() must implement RenderRaw.
Attributes
Attributes go in parentheses after the element name.
let frag = html! ;
Underscore-to-hyphen conversion applies to attribute names too:
// Renders as hx-target="body"
let frag = html! ;
assert_eq!;
Use string literals for attribute names that need special characters:
let frag = html! ;
assert_eq!;
Optional attributes
Append ? to the attribute name (before the :) to make it conditional. The attribute is only rendered when the
value is Some(_) (for Option) or true (for bool).
let class = Some;
let disabled = false;
let frag = html! ;
assert_eq!;
Values for ? attributes must implement RenderMaybeAttributeEscaped (or RenderMaybeAttributeRaw when used
with #()).
Control flow
Standard Rust if/else, if let, for, and match work inside templates:
let items = vec!;
let show_header = true;
let frag = html! ;
let value = Some;
let frag = html! ;
let tag = "div";
let frag = html! ;
Let bindings
Compute intermediate values within templates:
let world = "World";
let frag = html! ;
assert_eq!;
Nesting fragments
HtmlFragment implements RenderEscaped, so fragments can be embedded in other fragments:
let inner = html! ;
let outer = html! ;
assert_eq!;
Components
Components are reusable template functions defined with the component! macro:
use ;
component!
The macro generates a struct and a Component trait implementation. Components are
called with @ syntax inside html!:
let page = html! ;
assert_eq!;
In the component call, props appear before the ;, and extra HTML attributes appear after. The component body uses
#attrs to spread those extra attributes and #children to render the child content.
Passing fragments as props
Use PartialHtml as a prop bound to accept html! output as a component prop:
component!
let page = html! ;
Primitive props
Component props are received as references. For primitive types like bool or u32, dereference with * in the
component body:
component!
CSS classes
The classes! macro combines multiple class values, automatically skipping empty strings and None values:
let extra: = None;
let frag = html! ;
assert_eq!;
Values passed to classes! must implement the Class trait. This is implemented for &str, Option<T> where
T: Class, and Classes<T>(Classes).
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.