Skip to main content

Crate plait

Crate plait 

Source
Expand description

A lightweight, type-safe HTML templating library for Rust.

Plait provides a macro-based DSL for writing HTML directly in Rust code with compile-time validation and automatic escaping. It’s designed for building server-side rendered HTML with minimal runtime overhead.

§Quick Start

use plait::{html, render};

let name = "World";
let html = render(html! {
    div(class: "greeting") {
        h1 { "Hello, " (name) "!" }
    }
});

assert_eq!(html, "<div class=\"greeting\"><h1>Hello, World!</h1></div>");

§The html! Macro

The html! macro provides a concise syntax for writing HTML:

use plait::{html, render};

let html = render(html! {
    // Elements with attributes
    div(id: "main", class: "container") {
        // Nested elements
        h1 { "Title" }

        // Self-closing void elements
        br;
        input(type: "text", name: "query");

        // Text content and expressions (escaped by default)
        p { "Static text and " (2 + 2) " dynamic values" }
    }
});

§Expressions

Use parentheses to embed expressions. Content is automatically HTML-escaped:

use plait::{html, render};

let user_input = "<script>alert('xss')</script>";
let html = render(html! { p { (user_input) } });

assert_eq!(html, "<p>&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;</p>");

For raw, unescaped content, prefix with #:

use plait::{html, render};

let trusted_html = "<strong>Bold</strong>";
let html = render(html! { div { #(trusted_html) } });

assert_eq!(html, "<div><strong>Bold</strong></div>");

§Attributes

Attributes support several forms:

use plait::{html, render};

let class = Some("active");
let disabled = true;

let html = render(html! {
    // Boolean attribute (no value)
    button(checked) { "Checked" }

    // Optional attribute with `?:` - included when Some or true
    div(class?: class) { "Has class" }
    button(disabled?: disabled) { "Disabled" }

    // Attribute names with underscores become hyphens
    div(hx_target: "body") {}  // renders as hx-target="body"

    // String attribute names for special characters
    div("@click": "handler()") {}
});

§Control Flow

Standard Rust control flow works naturally:

use plait::{html, render};

let show = true;
let items = vec!["a", "b", "c"];
let variant = "primary";
let user = Some("Alice");

let html = render(html! {
    // Conditionals
    if show {
        p { "Visible" }
    } else {
        p { "Hidden" }
    }

    // if let with else
    if let Some(name) = user {
        p { "Welcome, " (name) "!" }
    } else {
        p { "Please log in" }
    }

    // Loops
    ul {
        for item in &items {
            li { (item) }
        }
    }

    // Pattern matching
    match variant {
        "primary" => button(class: "btn-primary") { "Primary" },
        "secondary" => button(class: "btn-secondary") { "Secondary" },
        _ => button { "Default" }
    }
});

§Components

Create reusable components using the component! macro:

use plait::{component, html, render};

component! {
    fn Button<'a>(class: &'a str) {
        button(class: format_args!("btn {class}"), #attrs) {
            #children
        }
    }
}

let html = render(html! {
    // Component props before `;`, HTML attributes after
    @Button(class: "primary"; id: "submit-btn", disabled?: false) {
        "Click me"
    }
});

assert_eq!(html, "<button class=\"btn primary\" id=\"submit-btn\">Click me</button>");

Inside components, #attrs spreads additional HTML attributes and #children renders the component’s children.

§URL Safety

URL attributes (href, src, action, etc.) are automatically validated. Dangerous schemes like javascript: are stripped:

use plait::{html, render};

let html = render(html! {
    a(href: "javascript:alert('xss')") { "Click" }
});

assert_eq!(html, "<a>Click</a>");  // href removed

Use #(...) for raw URLs when you trust the source.

§Performance

For better performance when output size is predictable, use render_with_capacity to pre-allocate the buffer:

use plait::{html, render_with_capacity};

let html = render_with_capacity(4096, html! {
    div { "Content" }
});

Macros§

component
A procedural macro for defining reusable HTML components.
html
A procedural macro for generating type-safe HTML with embedded Rust expressions.

Structs§

Html
A wrapper type representing safe, pre-rendered HTML content.
HtmlFormatter
A formatter for building HTML content safely and efficiently.

Traits§

Component
A trait for creating reusable HTML components.
MaybeAttr
A trait for values that can be conditionally written as HTML attributes.

Functions§

render
Renders HTML content using the provided closure.
render_with_capacity
Renders HTML content with a pre-allocated buffer capacity.