A safe HTML DSL for Rust and WebAssembly

Build HTML in plain Rust.

Molt gives you a fluent builder and an html! macro, escapes everything once on entry, and renders a plain string. Zero dependencies, about 25 KB of WebAssembly, and a static site you can host anywhere.

cargo add molt-framework

MIT licensed. Version 1.x, so what compiles today keeps compiling.

main.rs
use molt::prelude::*;

fn main() {
    let name = "<script>alert(1)</script>";

    let card = div()
        .add_class("card")
        .child(h1().text("Hello, Molt"))
        .child(p().text(name));

    println!("{}", card.render());
    // <div class="card"><h1>Hello, Molt</h1><p>&lt;script&gt;alert(1)&lt;/script&gt;</p></div>
}
A complete page in a handful of lines. Run it and you get HTML.

Safe by default, small by design

Template engines make you remember to escape. Runtimes make you ship kilobytes you never asked for. Molt does neither: it is only the HTML layer, and it plugs into the rest of your stack through a String.

  • Safe by construction

    Text and attribute values are escaped once, where they enter. A javascript: URL is written as #. There is no way to forget.

  • Zero dependencies

    cargo tree on the default features shows one crate: Molt. wasm-bindgen and rayon are opt-in.

  • Builder or macro

    Use the fluent builder or the html! macro. The macro expands to the same builder calls, never to a second code path.

  • Static site generator included

    Write pages and assets to disk, with sitemaps and RSS feeds. The output is plain HTML you can host anywhere.

  • Accessibility audit

    Audit the tree before the page ships: images without alt text, buttons and links without a name, and more.

  • Calm under hostile input

    A 100,000-level tree renders without crashing the process, and golden fixtures pin the output byte for byte, so a markup change is a failing test.

Fast enough that it is never the bottleneck

Numbers from the project's own benchmarks and test suite, measured on the development machine.

  • ≈ 400 µsto render a 10,000-item list

    Compact mode, measured with Criterion.

  • ≈ 25 KBgzipped WebAssembly build

    Opt in with the wasm feature.

  • 0mandatory dependencies

    The default build is one crate.

  • forbidunsafe code

    The crate sets unsafe_code to forbid.

Hosting is the easy part

Molt renders a string and the static site generator writes plain HTML. There is no server to run and nothing to keep warm. This very site is built with Molt.

  1. Run cargo run and get a dist folder of plain HTML.
  2. Push the project to GitHub.
  3. Let a static host publish it. This site uses AWS Amplify, which builds on every push.

The same core, in the browser

Build with wasm-pack and call Molt from JavaScript. Same renderer, same escaping rules, about 25 KB gzipped.

Read the WebAssembly guide

Build it, then call it from JavaScript.

Terminal
$ cargo install wasm-pack
$ wasm-pack build --target web --features wasm
# writes pkg/molt.js and pkg/molt_bg.wasm
# serve the folder over HTTP, ES modules need it
$ python3 -m http.server
main.js
import init, { element, WDocument } from "./pkg/molt.js";
await init();

const page = new WDocument("en")
  .addBody(element("h1").text("Hello, Molt"))
  .addBody(element("p").text("Same renderer."));

document.body.innerHTML = page.render();

Your first page is one cargo add away

Follow the Getting Started guide, or browse the full API reference on docs.rs.