handlebars-rust
A native Rust implementation of the Handlebars templating language for rendering JSON data into HTML or plain text.
Repository Health
Technical Analysis
handlebars-rust ports the Handlebars templating language to Rust, giving applications a logic-light way to turn structured data into HTML, emails, config files, or any other text output. Templates are compiled and cached in a central Handlebars registry, then rendered against any Serialize-able Rust value with built-in control flow (#if, #each, #with), partials, and template inheritance.
Beyond the core language, the crate adds Rust-specific extensions: a strict mode that turns missing-field lookups into typed errors instead of silent blanks, directory- and embedded-asset-based template sources with dev-mode hot reload, and an optional Rhai scripting integration so custom helpers can be written without recompiling the host application. It compiles to native code and WebAssembly, and is used as the templating layer behind several Rust web framework integrations.
What You Get
- A
Handlebarsregistry that compiles and caches named templates for repeated rendering - Built-in control-flow helpers (
#if,#each,#with,#unless,#lookup) matching the Handlebars.js spec - A pluggable helper system for writing custom block or inline helpers in Rust, or in Rhai script via the
script_helperfeature - Template inheritance and partials for composing shared layouts (headers, footers, page skeletons)
- Strict mode that converts missing-field template lookups into typed
RenderErrors instead of silently rendering blanks - Directory- and embedded-asset-based template sources with
dev_modehot reload for local development - WebAssembly compilation support, including an online playground for testing templates against both handlebars-rust and handlebars.js
Common Use Cases
- Rendering HTML pages or partials from a Rust web server (Actix, Warp, Axum, Rocket) using shared page layouts
- Generating transactional emails or notification text from structured application data
- Producing configuration files, CLI output, or codegen artifacts from JSON/YAML input at build or run time
- Embedding user-defined custom helpers written in Rhai script so non-Rust template logic can be changed without a rebuild
- Shipping template rendering into a browser or edge environment via the crate’s WebAssembly target
Under The Hood
Architecture
The crate is organized around a central Registry (re-exported as Handlebars) that owns compiled Templates, registered helper and decorator trait objects, and pluggable Source implementations for loading templates from files, directories, or embedded assets (src/registry.rs, src/sources.rs). Parsing goes through a PEG grammar defined in src/grammar.pest and compiled by pest/pest_derive into an AST consumed by src/template.rs; rendering walks that AST via the Renderable trait and a RenderContext (src/render.rs) that tracks local variables, block scope, and indentation, writing through an Output abstraction (src/output.rs) that can target a String, a file, or any Write. Data access is mediated by src/context.rs and the src/json/ module, which resolve dotted/indexed paths against serde_json::Value without requiring the caller to pre-flatten their data. Built-in control structures (if, each, with, lookup, raw, log) are themselves implemented as ordinary HelperDef trait objects in src/helpers/, so the same extension API used by library consumers also implements the core language — a clean, self-consistent layering with no privileged internal shortcut.
Tech Stack
Built on Rust 2024 edition (rust-version 1.85), using pest/pest_derive for grammar-driven parsing, serde/serde_json for the data model, and thiserror for typed, non-string error types (RenderError, TemplateError, ScriptError). Optional feature-gated dependencies extend the core: walkdir for directory template sources, rust-embed for compiling templates into the binary, rhai for the scripting-based helper system, and heck for case-conversion string helpers. Dev dependencies include criterion for benchmarking, tiny_http for example servers, and tempfile/env_logger for tests. The crate builds to both native targets and WebAssembly (build-wasm.sh, wasm/, playground/), and CI (GitHub Actions) runs the full matrix across stable/beta/nightly Rust on Linux, macOS, and Windows.
Code Quality
Testing is extensive: a tests/ directory holds more than a dozen integration suites covering block-scoped context, helper macros and lifetimes, subexpressions, whitespace control, and template embedding, backed by a templates/ fixture directory, alongside runnable doctests embedded directly in src/lib.rs and src/helpers/mod.rs. CI enforces cargo clippy --all-features --all-targets -- -D warnings (lint failures block merges) plus cargo test --all-features on every matrix target, and reports coverage to Coveralls via cargo-tarpaulin. Errors are modeled as typed enums via thiserror rather than strings or panics, and rustfmt.toml keeps formatting consistent. There is no separate CONTRIBUTING.md, but the README documents the contribution and maintainer-recruitment process directly.
What Makes It Unique
Rather than embedding or binding to the JavaScript Handlebars engine, this crate reimplements the language natively against a formal PEG grammar, which lets it offer compile-checked template registration and a strict mode that turns the original spec’s silent-failure-on-missing-field behavior into a typed Rust error — a deliberate deviation from handlebars.js semantics aimed at Rust’s error-handling conventions. Its optional Rhai scripting integration is unusual for a templating library: it lets consumers define custom helpers in a sandboxed script language at runtime instead of only as compiled Rust functions, and the project ships a WebAssembly-compiled browser playground built from master on every push so users can directly compare rendering behavior against handlebars.js.