A headless Rust implementation of Mermaid for parsing, layout, and rendering.
Quick start · One-shot and reuse · Output targets · Cargo features · Ecosystem · Compatibility
merman is the main Rust crate in this repository. It parses Mermaid source into a typed semantic
model, computes layout, and renders SVG. Optional features add diagnostics, editor facts,
ASCII/Unicode output, PNG, JPEG, and PDF. The native path does not start Node.js, Puppeteer,
Chromium, or another JavaScript runtime.
For incremental editor syntax, the repository also publishes tree-sitter-mermaid: a tolerant
grammar and query package for Rust, Node.js, browser Workers, and editor integrations.
Merman currently follows mermaid@11.16.1. Its parser, layout, configuration, theming,
sanitization, and SVG structure are checked against pinned Mermaid source and fixtures.
Note
This README documents the current main branch. The operation-scoped Renderer API was
introduced after the published 0.8.0-alpha.5 tag. If you depend on that release, use its
tagged README.
Used by Zed. Zed uses Merman as its Rust Mermaid backend. Read the merged integration.
Run the maintained SVG example from a source checkout:
cargo run --locked -p merman --example render_svg > diagram.svgThe same operation in Rust is:
use merman::{OperationControl, RenderOutput, RenderRequest, Renderer, SvgRequest};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let output = Renderer::new().render(RenderRequest::svg(
"flowchart TD\n A[Start] --> B[Done]",
OperationControl::new(),
SvgRequest::default(),
))?;
let RenderOutput::Svg(Some(svg)) = output else {
return Err("no Mermaid diagram detected".into());
};
print!("{}", svg.svg());
Ok(())
}The selected RenderOutput variant contains None when the source has no Mermaid diagram.
Cancellation, resource exhaustion, parse errors, and unavailable output capabilities remain
separate structured errors.
A Renderer stores defaults. Each RenderRequest describes one independent operation.
| Type | Role | Typical lifetime |
|---|---|---|
Renderer |
Holds the engine and shared defaults | One call or many calls |
RenderRequest |
Borrows the source and selects a target, overrides, and control | One operation |
OperationControl |
Carries cooperative cancellation and an optional deadline | One operation; clone it for the cancelling task or thread |
SemanticArtifact |
Pairs the parsed render model with its operation context | Inspect it, then consume it into one output target |
The quick start is the one-shot form: create a renderer, issue one request, and drop it. Merman does
not hide this behind a separate source-to-SVG helper; one-shot and repeated work both use
Renderer::render().
For a batch or service, keep one configured Renderer when inputs share parse options, runtime
policy, site config, or resource limits. It shares settings, not parsed state: every call parses its
own source and carries a fresh RenderRequest and, normally, a fresh OperationControl.
If several SVGs will share one DOM, give every request a diagram_id that remains unique after
merman::svg::sanitize_svg_id() normalization. If an editor needs to retain analysis between
queries, use a host-owned snapshot from merman::editor; a reused renderer is not an editor
session.
The self-contained Rust examples cover these cases: render_svg.rs is one-shot,
render_many.rs reuses a configured renderer, and embed_multiple_svgs.rs assigns stable IDs
for a shared document.
Choose one typed target for each request:
| Need | Start with | Cargo feature |
|---|---|---|
| Parse a typed Mermaid model | Engine and ParseOptions |
Always available |
| Prepare or inspect the semantic artifact | Renderer::prepare_semantic() or RenderTarget::Semantic |
Always available |
| Render Mermaid-style SVG | RenderRequest::svg() |
svg |
| Inspect layout JSON or an SVG capability plan | RenderRequest::layout_json() or RenderRequest::svg_plan() |
svg |
| Render terminal text for supported families | RenderRequest::ascii() |
ascii |
| Export PNG, JPEG, or PDF | RenderRequest::png(), jpeg(), or pdf() |
matching output feature |
| Produce diagnostics or analyze Markdown and MDX fences | merman::analysis::Analyzer |
analysis |
| Build parser-backed editor snapshots | APIs under merman::editor |
editor |
Cargo features remove unavailable target types at compile time. Within a compiled target, a
missing layout engine or runtime adapter returns a typed missing-capability error instead of
silently choosing a different result.
The default merman dependency enables complete-svg: SVG rendering, Cytoscape and ELK layouts,
and math labels. Analysis, editor APIs, terminal output, binary export, and ambient system adapters
remain opt-in.
Cargo features select capabilities and output backends, not Mermaid diagram families. Every parser-capable build retains the same language catalog.
| Goal | Cargo selection |
|---|---|
| Complete deterministic SVG | defaults, or complete-svg |
| Basic SVG without optional layout engines or math | default-features = false, features = ["svg"] |
| Diagnostics and editor APIs | default-features = false, features = ["analysis", "editor"] |
| Terminal output | default-features = false, features = ["ascii"] |
| Binary export | Add only the required png, jpeg, or pdf feature |
The capability guide documents feature forwarding, artifact profiles, system adapters, and
resource policy. Parser-only applications can depend on merman-core directly. Applications that
need lower-level layout or SVG pipeline control can use the re-exports under merman::svg or depend
on merman-render.
Renderer::new() uses deterministic engine defaults. SvgRequest::default() supplies the default
headless SVG environment. System clock, time-zone, random, and timing adapters are separate Cargo
features and must also be selected explicitly at runtime.
Every render request owns an OperationControl. Its clones share cooperative cancellation and an
optional monotonic deadline. Cancellation is observed at operation checkpoints; a synchronous host
callback already in progress may return before Merman reaches the next checkpoint.
Resource limits are part of the request contract. Missing capabilities and exhausted limits return typed errors rather than partial output or a silent fallback. See the resource and options guide for the complete policy model.
Mermaid source
|-- tree-sitter-mermaid
| `-- tolerant CST ---------------------> highlighting, folding, syntax selection
|
`-- Merman semantic parser
|-- typed model ----------------------> diagnostics, navigation, refactoring
|-- typed layout ---------------------> Mermaid-style SVG
|-- validated SVG --------------------> PNG, JPEG, and PDF
`-- supported typed diagram models ---> ASCII and Unicode
The two parsers have different contracts. Tree-sitter keeps useful syntax structure while a document is incomplete; Merman remains the strict semantic and rendering authority. The semantic model is shared by analysis and rendering. Binary export starts from validated SVG, not a browser screenshot.
| Architecture | Mindmap | Sankey |
|---|---|---|
![]() |
![]() |
![]() |
These examples were rendered headlessly by merman-cli, which uses the same Rust parser and
rendering pipeline. The Playground covers all 35 built-in diagram families.
Choose the surface that owns the job instead of pulling the complete renderer into every host:
| Need | Start with |
|---|---|
| Parse, lay out, and render from Rust | merman |
Run shell commands, Markdown batches, linting, or mmdc compatibility |
merman-cli |
| Use WebAssembly in a browser or Worker | Browser packages |
| Use native Node.js bindings | Node.js package |
| Build incremental CSTs, syntax highlighting, folding, or selections | tree-sitter-mermaid on crates.io or @mermanjs/tree-sitter-mermaid on npm |
| Add diagnostics, completion, navigation, and rename | merman-lsp or the VS Code extension |
| Integrate C/C++, Python, Flutter, Android, Apple, Typst, or other delivery surfaces | Package surface guide |
tree-sitter-mermaid is independently versioned because editor syntax trees and queries have a
different compatibility contract from Merman's semantic model and renderer. Its package README
covers Node.js, browser, Rust, C/C++, query, and downstream-editor integration.
The documentation index covers architecture records, contributor procedures, parity evidence, and release operations. The Typst package and other independently delivered integrations remain listed in the package surface guide.
Merman offers two static-SVG paths for Rustdoc; neither loads JavaScript or fetches diagrams when a reader opens the generated documentation.
| Choose | When |
|---|---|
merman-cli Rustdoc guide |
Generate and commit checked Markdown fragments without adding a renderer to the documented crate's Cargo graph |
merman-rustdoc |
Render annotated Mermaid blocks during cargo doc through an opt-in procedural macro and native renderer closure |
The dedicated guides cover configuration, CI freshness, docs.rs, packaging, generated ownership, and migration. The two paths are explicit alternatives; neither silently falls back to the other.
Merman aims for source-backed agreement in parsing, semantic models, layout, configuration, theming, sanitization, and SVG DOM structure. It does not promise byte-for-byte Chromium pixels.
Browser font fallback, getBBox() floats, foreignObject, HTML labels, and RoughJS path geometry
can still produce documented differences where a robust headless equivalent is unavailable.
Mermaid-style SVG may contain HTML labels. Use SvgPipeline::resvg_safe() or a typed PNG, JPEG, or
PDF target when the consumer cannot render foreignObject.
Read the alignment dashboard, SVG output pipeline, rendering security guide, and benchmark methodology for the current evidence boundary.
cargo nextest run --workspace
cargo fmt --all -- --check
cargo run -p xtask -- verify --strictThe strict gate checks generated contracts, all-family SVG evidence, package surfaces, browser tests, and release legal material against the pinned reference bundle.
Merman is available under the Apache License 2.0 or MIT License.
Source translations, fixtures, embedded resources, behavioral references, and their exact
revisions are recorded in THIRD_PARTY_NOTICES.md and the
machine-readable component inventory.
Merman is independent of, and not affiliated with, endorsed by, or sponsored by the Mermaid project or its maintainers.


