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.
Shorthand props
When a variable has the same name as a component prop, you can use shorthand syntax - just like Rust struct initialization:
let class = "primary";
// These are equivalent:
let a = html! ;
let b = html! ;
assert_eq!;
Shorthand and explicit props can be mixed freely:
let name = "Alice";
let html = html! ;
assert_eq!;
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).
Web framework integrations
Plait provides optional integrations with popular Rust web frameworks. Both Html and HtmlFragment can be
returned directly from request handlers when the corresponding feature is enabled.
Enable integrations by adding the feature flag to your Cargo.toml:
[]
= { = "0.8", = ["axum"] }
Available features: actix-web, axum, rocket.
axum
Html and HtmlFragment implement
IntoResponse(https://docs.rs/axum/latest/axum/response/trait.IntoResponse.html):
use ;
use ;
async
let app = new.route;
You can also return an HtmlFragment directly without calling .to_html():
async
actix-web
Html and HtmlFragment implement
Responder(https://docs.rs/actix-web/latest/actix_web/trait.Responder.html):
use ;
use ;
async
rocket
Html and HtmlFragment implement
Responder(https://docs.rs/rocket/latest/rocket/response/trait.Responder.html):
use get;
use ;
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.