Mermaider 0.15.1
dotnet add package Mermaider --version 0.15.1
NuGet\Install-Package Mermaider -Version 0.15.1
<PackageReference Include="Mermaider" Version="0.15.1" />
<PackageVersion Include="Mermaider" Version="0.15.1" />
<PackageReference Include="Mermaider" />
paket add Mermaider --version 0.15.1
#r "nuget: Mermaider, 0.15.1"
#:package Mermaider@0.15.1
#addin nuget:?package=Mermaider&version=0.15.1
#tool nuget:?package=Mermaider&version=0.15.1
<p align="center"> <img src="https://raw.githubusercontent.com/nullean/mermaider/main/nuget-icon.png" alt="Mermaider" width="96" /> </p>
<h1 align="center">Mermaider</h1>
<p align="center"> Render <a href="https://mermaid.js.org/">Mermaid</a> diagrams to SVG in pure .NET.<br/> No browser. No DOM. No JavaScript runtime. AOT-ready. </p>
<p align="center"> <a href="https://www.nuget.org/packages/Mermaider"><img src="https://img.shields.io/nuget/v/Mermaider.svg" alt="NuGet" /></a> <a href="https://github.com/nullean/mermaider/actions"><img src="https://github.com/nullean/mermaider/actions/workflows/ci.yml/badge.svg" alt="CI" /></a> </p>
Table of Contents
- Why Mermaider?
- Quick Start
- Theming
- Render Options
- Strict Styling
- SVG Sanitization
- Text output
- CLI
- MSAGL Layout Provider
- AOT Support
- Benchmarks
- Building from Source
- Supported Diagrams
- Attribution
- Projects using Mermaider
- License
Why Mermaider?
Mermaider is a complete Mermaid parser, layout engine, and SVG renderer built entirely in .NET. Hand it a Mermaid string; get a sanitized, consistently styled SVG back. No interop, no child processes, no headless browsers.
It covers all 24 major Mermaid diagram types, always-on allowlist SVG sanitization with no opt-out,
and a unified design-token model so every diagram type renders from the same RenderOptions — one API,
one theming model, consistent output regardless of diagram type.
What makes it stand out
- Pure .NET, zero interop: just a NuGet reference. No Chromium, no Node.js, no subprocess management.
- Native AOT: every public API proven in CI on Linux, macOS, and Windows.
- Built-in layout engine: zero-dependency layered layout with compound subgraphs and an orthogonal edge router.
- 24 diagram types: one API, one theming model for all of them.
- Unified theming: 15 themes, live-switchable via CSS custom properties.
- Always-on SVG sanitization: allowlist-only, no opt-out.
- Strict styling mode: enforce your design system on user-authored diagrams.
- Fast: ~95 µs, ~209 KB allocated to render a simple flowchart end to end.
Pure .NET parsing and rendering
Mermaider parses Mermaid's text DSL and renders SVG output using only managed .NET code. There is no dependency on JavaScript, Chromium, or any external process. This means deterministic output, no cold-start penalty, and trivial deployment: just a NuGet reference.
Built-in layout engine
Flowchart, state, class, ER and requirement diagrams need a graph layout to place nodes and route edges. Mermaider
ships its own engine for them, the zero-dependency Sugiyama package. It is the default and the
recommended engine. Architecture diagrams use a directional-grid layout, because their edges name explicit sides.
Every other diagram type uses layout arithmetic built into its renderer.
The package name refers to the layered (Sugiyama) framework, the same family as Graphviz dot, dagre and ELK
Layered. The engine goes well beyond the textbook framework:
- Ranking. Network simplex, plus a nesting graph that keeps each subgraph in one band of layers.
- Ordering. Barycentre sweeps, then swap and insertion refinement, then deterministic restarts.
- Placement. Brandes–Köpf coordinates on evenly spread ports, with a column reserved for every edge label.
- Compound layout. Each subgraph is laid out on its own and placed as one node of its parent, so subgraph boxes never overlap.
- Routing. An obstacle-aware orthogonal router. It uses the label columns as routes and falls back to an A* grid search that penalises crossings, overlaps, near-parallel runs and foreign subgraphs, with rip-up passes.
The Sugiyama README and the layout docs describe each phase.
The engine is also much leaner than Microsoft MSAGL, the layout backend evaluated during early development. On a simple 6-node flowchart:
| Phase | MSAGL | Built-in Sugiyama | Improvement |
|---|---|---|---|
| Layout only | 226 µs / 549 KB | 24 µs / 82 KB | 9.6× faster, 6.7× less memory |
| End-to-end render | 423 µs / 683 KB | 109 µs / 209 KB | 3.9× faster, 3.3× less memory |
The layout-only row runs each layout provider on the same parsed flowchart: node sizing, layering, placement and edge routing (the built-in side includes the compound layout and the orthogonal router). The end-to-end row adds parsing, rendering and sanitizing.
MSAGL remains available as an optional, legacy-compatible provider. It does not support several features of the built-in engine; see MSAGL Layout Provider.
Native AOT
Every public API is compatible with .NET Native AOT. The CI pipeline publishes and invokes a native binary on Linux, macOS, and Windows to prove it. No reflection, no runtime code generation, no surprises.
Security and normalized styling
Security and visual consistency are not afterthoughts when embedding user-authored diagrams. Both shaped Mermaider's design from the start.
Safety
Every rendered SVG passes through an element/attribute allowlist before leaving the library. There is no way to opt out. The allowlist is the only gate:
<script>,<foreignObject>, and event handlers are absent from the allowlist, not pattern-matched- External
hrefURIs are structurally excluded; the only permittedhrefis a base64data:image/svg+xmlordata:image/pngon an<image>element - A second sanitizer pass on any output is always a no-op, proving convergence
Coverage: unit tests and a deterministic fuzzer with 4,000 generated cases across mutation and structured element/attribute/value cross-products.
Visual consistency
All 24 diagram types render from the same RenderOptions. A single set of values controls:
- Colors:
Bg,Fg,Accent,Muted,Surface,Border,Line - Style preset:
Style(Quiet, Blueprint, Tonal) plusGradient,TintandElevation - Typography:
Font,MonoFont,FontSizeand size ratios - Data palette: categorical colors for pie, sankey, timeline, gitgraph, and the rest
There is no per-type color system or per-type font stack. The design token model is the only model.
Strict Styling goes further for user-authored content: classDef, style, linkStyle, and theme overrides are stripped (and optionally reported via callback), and nodes are constrained to a class allowlist you define.
Quick Start
dotnet add package Mermaider
using Mermaider;
var svg = MermaidRenderer.RenderSvg("""
graph TD
A[Start] --> B{Decision}
B -->|Yes| C[OK]
B -->|No| D[End]
""");
Theming
Every diagram derives its palette from just two colors (background and foreground) using
color-mix() CSS functions embedded in the SVG. Override individual roles for richer themes:
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
Bg = "#1E1E2E",
Fg = "#CDD6F4",
Accent = "#CBA6F7", // arrow heads, highlights
Muted = "#6C7086", // secondary text, edge labels
});
Because the SVG uses CSS custom properties, themes switch live without re-rendering: just update the
--bg / --fg properties on the root <svg> element.
To pin the paint of ordinary boxes instead of deriving it, set Surface (fill) and Border (outline). They apply to
boxes in the Default colour family in every style; other clusters, role classes, decisions, terminals, containers
and charts keep their derived colours. Unless Line is set, connectors follow Border.
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
Surface = "#FEFCE8", // fill of ordinary boxes
Border = "#A16207", // their outline, and the line colour unless Line is set
});
Style presets
Three presets restyle every diagram type without changing layout: Quiet (default; tinted gradient boxes, strip headers, outlined label pills), Blueprint (outline-first technical drawing, square corners, dashed containers, mono captions, no shadows) and Tonal (soft filled blocks, no outlines, big radii, chip headers).
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
Style = DiagramStyle.Tonal,
Gradient = false, // flat fills
Tint = 0.8, // 0.5–1.5, softer tints (useful on dark themes)
Elevation = 2, // 0 none, 1 default, 2 adds an ambient shadow
});
See Theming and DESIGN.md for the full knob table.
Built-in themes
15 themes ship out of the box. Pass the name via the theme init directive in your diagram source, or
resolve one programmatically:
var colors = Themes.BuiltIn["tokyo-night"];
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
Bg = colors.Bg, Fg = colors.Fg, Accent = colors.Accent, Muted = colors.Muted,
});
| Theme | Style |
|---|---|
zinc-light |
Default light |
zinc-dark |
Default dark |
tokyo-night / tokyo-night-storm / tokyo-night-light |
Tokyo Night family |
catppuccin-mocha / catppuccin-latte |
Catppuccin |
nord / nord-light |
Nord |
dracula |
Dracula |
github-light / github-dark |
GitHub |
solarized-light / solarized-dark |
Solarized |
one-dark |
One Dark |
Dark themes automatically ship a brighter data palette (pie slices, sankey nodes, timeline bands, etc.) that maintains legibility on dark backgrounds. You can override the data palette entirely:
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
DataPalette = ["#ff6b6b", "#feca57", "#48dbfb", "#ff9ff3", "#54a0ff"],
});
Render Options
| Option | Type | Default | Description |
|---|---|---|---|
Bg |
string? |
"#FFFFFF" |
Background color (hex or CSS) |
Fg |
string? |
"#27272A" |
Foreground / primary text color |
Line |
string? |
default box border | Edge/connector stroke colour; unset, it follows the border of an ordinary box (Border when set, else the Default role's outline) |
Accent |
string? |
derived | Arrowheads, highlights |
Muted |
string? |
derived | Secondary text, edge labels |
Surface |
string? |
unset | Optional solid fill for ordinary boxes (the Default family): first-cluster nodes, entity headers, participants, services. Wins in every style. No theme sets it |
Border |
string? |
unset | Optional outline for the same boxes, at the style's outline width (Tonal draws none). Lines follow it unless Line is set. No theme sets it |
Font |
string? |
"Inter" |
Font family for all text |
MonoFont |
string? |
system stack | Monospace font for ER attribute types and Class member signatures |
FontSize |
string? |
"1rem" |
Base font size (--fs-m). Accepts px, rem, em, and % units |
FontSizeSmall |
double? |
0.875 |
Ratio for small text (--fs-s) |
FontSizeExtraSmall |
double? |
0.75 |
Ratio for extra-small text (--fs-xs) |
FontSizeLarge |
double? |
1.125 |
Ratio for large text (--fs-l) |
DataPalette |
string[]? |
theme default | Categorical colors for pie, sankey, timeline, gitgraph, radar, mindmap, venn, journey, packet, xychart, treemap; flowchart/state/ER/class auto colouring uses it minus the role hues |
Default |
string? |
theme's default box colour (slate in the zinc themes), else the first non-role palette colour | Colour of an ordinary box (first cluster of nodes, entities, classes) |
Success / Failure / Warning / Info |
string? |
palette green / red / yellow / blue | Semantic role colours (class success, failure, warning, info); auto colouring avoids the first three |
AllowedDiagrams |
DiagramTypes |
DiagramTypes.All |
Allowlist of accepted diagram types; diagrams outside this set throw MermaidParseException |
Style |
DiagramStyle |
Quiet |
Style preset: Quiet, Blueprint or Tonal. Paint only; layout is the same |
Gradient |
bool |
true |
Gradient fills on boxes, containers and bars; false = flat fills |
Tint |
double? |
1 |
Strength of every derived tint, clamped to 0.5–1.5 |
Elevation |
int? |
1 |
Shadows: 0 none, 1 boxes + containers, 2 adds ambient. Ignored by Blueprint |
RoundedEdges |
bool |
true |
Rounded bends on edge paths (radius from the style preset) |
Transparent |
bool |
true |
Transparent background |
Padding |
double? |
40 |
Canvas padding in px |
NodeSpacing |
double? |
28 |
Horizontal spacing between sibling nodes |
LayerSpacing |
double? |
56 |
Vertical spacing between layers |
Strict |
StrictStylingOptions? |
null |
Optional host-controlled styling policy |
SanitizeMode |
SanitizeMode |
Strip |
Strip SVG violations, or throw MermaidSvgException in Block mode |
Font options
Both Font and MonoFont accept a font-family name (not an arbitrary CSS declaration):
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
Font = "system-ui", // sans-serif system font
MonoFont = "ui-monospace", // system monospace (e.g. SF Mono, Cascadia)
});
Generic CSS keywords (monospace, sans-serif, serif, etc.) are passed unquoted as required by CSS.
Named fonts are automatically quoted: 'Courier New', 'JetBrains Mono', etc.
Data palette
Color-encoded diagram types (pie, sankey, timeline, gitgraph, radar, mindmap, venn, journey, packet,
xychart, treemap) all draw from a single 12-color Tableau-derived palette. Dark themes ship a brightened
variant automatically. Use DataPalette to supply your own colors:
// Brand colors for pie/sankey/etc.
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
DataPalette = ["#0f62fe", "#da1e28", "#198038", "#f1c21b"],
});
The CategoricalPalette class exposes all 12 colors by semantic name for use in custom logic:
using Mermaider.Rendering;
// Named colors (index-stable)
string blue = CategoricalPalette.Blue; // #4e79a7
string red = CategoricalPalette.Red; // #e15759
string green = CategoricalPalette.Green; // #59a14f
// Dark variants (same hue, ~20% lower lightness — suitable for strokes/borders)
string redDark = CategoricalPalette.RedDark;
string blueDark = CategoricalPalette.BlueDark;
// Ordinal access (wraps at 12)
string color = CategoricalPalette.At(7); // Pink
Allowed diagram types
AllowedDiagrams is a [Flags] enum that controls which diagram types the renderer will accept.
Diagrams whose detected type is outside the set throw MermaidParseException. The default is
DiagramTypes.All.
// Stable diagrams only, plus Architecture:
var opts = new RenderOptions
{
AllowedDiagrams = DiagramTypes.Stable | DiagramTypes.Architecture,
};
// Everything except TreeView and Block:
var opts = new RenderOptions
{
AllowedDiagrams = DiagramTypes.All & ~(DiagramTypes.TreeView | DiagramTypes.Block),
};
// Only flowcharts and sequence diagrams:
var opts = new RenderOptions
{
AllowedDiagrams = DiagramTypes.Flowchart | DiagramTypes.Sequence,
};
Named sets:
| Set | Contents |
|---|---|
DiagramTypes.All |
All 24 diagram types (default) |
DiagramTypes.Stable |
15 types with stable Mermaid syntax (no -beta keyword) |
DiagramTypes.Beta |
9 types that use a -beta keyword (radar-beta, architecture-beta, etc.) |
Edge rounding
Edges use rounded corners by default (6px radius). To render straight/angular edges instead:
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
RoundedEdges = false,
});
Font sizing
Font sizes are emitted as CSS custom properties in the SVG <style> block:
:root { --fs-xs: 0.75rem; --fs-s: 0.875rem; --fs-m: 1rem; --fs-l: 1.125rem; }
All text elements reference these variables, so downstream consumers can override sizing by
redefining the custom properties on the <svg> element without re-rendering.
Strict Styling
Strict styling is about visual uniformity, not safety. It does not make output safe to publish. SVG sanitization does that, and it is always on regardless of this setting. Use strict styling when you want a consistent look controlled by your design system rather than by whatever colors a diagram author wrote.
When you embed user-authored Mermaid in a product, you typically want uniform styling controlled by your
design system, not arbitrary colors injected via classDef or style directives.
Strict styling:
- Strips
classDef,style, andlinkStyledirectives (diagram renders; violations reported viaOnStripped) - Strips source-authored
theme/themeVariablesoverrides from%%{init}%%and frontmatter - Strips C4
UpdateElementStyle/UpdateRelStyle/UpdateBoundaryStyle - Enforces a pre-approved class allowlist with theme-aware colors
- Generates
@media (prefers-color-scheme: dark)CSS for automatic light/dark switching - Auto-derives dark mode colors by inverting HSL lightness (or use explicit overrides)
The default mode is StrictStylingMode.Strip — disallowed directives and unknown class references are silently dropped and the diagram still renders. Wire OnStripped to log what was dropped:
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
Strict = new StrictStylingOptions
{
AllowedClasses =
[
new DiagramClass
{
Name = "ok",
Fill = "#D4EDDA", Stroke = "#28A745", Color = "#155724",
},
new DiagramClass
{
Name = "warn",
Fill = "#FFF3CD", Stroke = "#FFC107", Color = "#856404",
},
new DiagramClass { Name = "custom-highlight" },
],
OnStripped = v => logger.LogWarning("Strict mode stripped {Kind}: {Message}", v.Kind, v.Message),
}
});
Set Mode = StrictStylingMode.Block to throw MermaidParseException instead (the original fail-closed behavior):
Strict = new StrictStylingOptions
{
Mode = StrictStylingMode.Block,
AllowedClasses = [ ... ],
}
Nodes reference classes via Mermaid's ::: shorthand or class directive:
graph TD
A[Healthy]:::ok --> B[Warning]:::warn --> C[Custom]:::custom-highlight
SVG Sanitization
Sanitization is the safety mechanism, and it is always on. Every rendered SVG is run through an
element/attribute allowlist on every RenderSvg call before it leaves the library. There is no way to
turn it off, and it is completely independent of strict styling. This is defense-in-depth on
top of the per-renderer output escaping: even if a renderer had a bug, disallowed markup cannot reach a
published page.
The sanitizer is allowlist-only: anything not explicitly affirmed as safe is removed. There is deliberately
no blocklist of "known bad" constructs. Safety never depends on us having enumerated every dangerous
attribute. Because they are absent from the allowlist, the main XSS vectors are denied as a consequence:
<script>, <foreignObject>, on* event handlers, and href/xlink:href with javascript:/http(s):/
non-image data URIs. The single positive exception is a base64 data:image/svg+xml or data:image/png
URI href on an <image> element (used for diagram icons).
The renderer-only stylesheet is accepted through a separate exact generated grammar. Standalone
untrusted SVG cannot retain <style> elements or style attributes. Custom sanitizer allowlists can
only narrow the built-in safety sets; they cannot opt a new element or attribute into the policy.
Use RenderOptions.SanitizeMode to choose what happens when the output contains a violation (which, for the
built-in renderers, should never happen; it indicates a bug):
SanitizeMode.Strip(default): remove disallowed content from well-formed SVG and return the stripped document. If the generated output is not well-formed XML, returnMermaidRenderer.FallbackSvg, the canonical empty SVG document.SanitizeMode.Block: throwMermaidSvgExceptionwith all detected violations (fail closed).
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
SanitizeMode = SanitizeMode.Block, // fail closed instead of silently stripping
});
In Strip mode (the default), use OnSanitized to receive a callback for each removed violation instead of discovering them silently:
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
OnSanitized = v => logger.LogWarning("SVG sanitizer removed {Kind} '{Name}'", v.Kind, v.Name),
});
The same engine is also exposed standalone, useful beyond Mermaid for any untrusted SVG content:
var result = SvgSanitizer.Sanitize(untrustedSvg);
if (result.HasViolations)
Console.WriteLine($"Stripped {result.Violations.Count} violations");
var cleanSvg = result.Svg;
Malformed XML produces MermaidRenderer.FallbackSvg and a malformed-xml violation in the result.
To reject instead, call SvgSanitizer.Sanitize(untrustedSvg, SanitizeMode.Block); it throws
MermaidSvgException. A well-formed document with violations is always returned stripped by the
non-throwing overload; safe siblings are preserved.
<a name="text-output"></a>Text output
MermaidRenderer.RenderAscii(text, options?) draws a diagram as characters, for a terminal, a log or a
pull-request comment. Flowcharts and state diagrams go through the same Sugiyama layout as the SVG path, with
node sizes measured in character cells rather than pixels, so the layout lands on the grid rather than being
divided down onto it.
var text = MermaidRenderer.RenderAscii("""
flowchart LR
A[(OrderEvents)] -->|"2/s"| B{{Orders}}
B --> C[Order]
""");
┌────────┐ ┌───────┐
┌─────────────┐ │ Orders │ │ Order │
│ OrderEvents ├─2/s┤ ├────────────▶┤ │
└─────────────┘ └────────┘ └───────┘
AsciiOptions.Ascii drops to plain ASCII for a terminal that cannot be trusted with box-drawing characters;
Width bounds label truncation, Groups and EdgeLabels leave parts out.
xy charts are plotted on the same grid. A box series — five numbers, a min/q1/median/q3/max summary — is
drawn as a box-and-whisker row per category, in text and in SVG:
xychart-beta
title "Effect latency (s)"
x-axis [checkout, search]
box [0.01, 0.04, 0.08, 0.2, 1.4]
box [0.02, 0.03, 0.05, 0.09, 0.3]
Effect latency (s)
checkout ├▐┃━━▌────────────────────────────────┤
search ├┃▌─────┤
──────────────────────────────────────
0.01 1.4
CLI
dotnet tool install -g Mermaider.Cli
echo 'graph TD
A --> B' | mermaid > diagram.svg
mermaid input.mmd -o output.svg --theme github-dark
mermaid input.mmd -o output.svg --style blueprint --elevation 0
mermaid input.mmd -o output.svg --style tonal --no-gradient --tint 0.8
mermaid input.mmd --ascii # draw it as text
mermaid input.mmd --plain --width 80
mermaid --list-themes
<a name="msagl-layout-provider"></a>MSAGL Layout Provider
The optional Mermaider.Layout.Msagl package swaps in Microsoft MSAGL
for flowchart, state, class and ER diagrams. It exists for compatibility with output from earlier Mermaider versions.
The built-in engine is the recommended choice: it is faster, and MSAGL lacks the following.
- No compound layout. MSAGL lays out the nodes without their subgraphs. Subgraph boxes are drawn around the members afterwards and can overlap each other or unrelated nodes.
- No orthogonal router features. MSAGL uses its own rectilinear router. There are no label columns, no ports spread along node sides or on diamond and ellipse outlines, and edges written against a subgraph end on a member node rather than on the subgraph border.
- Missing class and state features. The MSAGL providers ignore class namespaces, class notes, lollipop interface targets and state-diagram notes.
- Not used for every graph diagram. Requirement diagrams always use the built-in engine, and so does the text output.
Node and box sizes come from the same code under both engines, so class, ER and flowchart text fits its boxes either way.
dotnet add package Mermaider.Layout.Msagl
using Mermaider.Layout.Msagl;
// Global — all subsequent renders use MSAGL:
MermaidRenderer.SetLayoutProvider(new MsaglLayoutProvider());
// Or per-call:
var svg = MermaidRenderer.RenderSvg(input, new RenderOptions
{
LayoutProvider = new MsaglLayoutProvider(),
});
AOT Support
Mermaider is fully compatible with .NET Native AOT. To publish your own AOT app:
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mermaider" />
</ItemGroup>
dotnet publish -c Release
Benchmarks
Graph-based diagram types use the built-in Sugiyama engine. Measured with [MemoryDiagnoser] on .NET 10
(Apple M2, BenchmarkDotNet medium run):
| Method | Mean | Allocated |
|---|---|---|
| Flowchart (simple) | ~95 µs | ~209 KB |
| Flowchart (large) | ~682 µs | ~829 KB |
| Sequence | ~98 µs | ~173 KB |
| State | ~110 µs | ~233 KB |
| Class | ~112 µs | ~219 KB |
| ER | ~208 µs | ~697 KB |
dotnet run --project tests/Mermaider.Benchmarks -c Release
Building from Source
git clone https://github.com/nullean/mermaider.git
cd mermaider
./build.sh build
./build.sh test
Supported Diagrams
Mermaider renders all major Mermaid diagram types to SVG. The design model is normalized across all
types. Every diagram respects the same Bg, Fg, Accent, Muted, Font, MonoFont, and
DataPalette options.
<p align="center"> <img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/playground.png" alt="Mermaider playground - all diagram types with theme controls" /> </p>
Flowchart
MermaidRenderer.RenderSvg("""
graph TD
A[Start] --> B{Decision}
B -->|Yes| C[OK]
B -->|No| D[Cancel]
C --> E[End]
D --> E
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/flowchart.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/flowchart.light.svg" alt="Flowchart" /></picture></p>
Sequence
MermaidRenderer.RenderSvg("""
sequenceDiagram
participant A as Alice
participant B as Bob
A->>B: Hello Bob!
B-->>A: Hi Alice!
A->>B: How are you?
B-->>A: Great, thanks!
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/sequence.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/sequence.light.svg" alt="Sequence" /></picture></p>
State
MermaidRenderer.RenderSvg("""
stateDiagram-v2
[*] --> Idle
Idle --> Processing : submit
Processing --> Success : ok
Processing --> Failed : error
Success --> [*]
Failed --> Idle : retry
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/state.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/state.light.svg" alt="State" /></picture></p>
Class
MermaidRenderer.RenderSvg("""
classDiagram
class Animal {
<<abstract>>
+String name
+eat() void
}
class Dog { +bark() void }
class Cat { +purr() void }
Animal <|-- Dog
Animal <|-- Cat
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/class.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/class.light.svg" alt="Class" /></picture></p>
ER (Entity-Relationship)
MermaidRenderer.RenderSvg("""
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE_ITEM : contains
CUSTOMER {
string name PK
string email UK
}
ORDER {
int id PK
date created
}
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/er.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/er.light.svg" alt="ER (Entity-Relationship)" /></picture></p>
Pie Chart
MermaidRenderer.RenderSvg("""
pie
title Pet Adoption
"Dogs" : 386
"Cats" : 85
"Rats" : 15
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/pie.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/pie.light.svg" alt="Pie Chart" /></picture></p>
Quadrant Chart
MermaidRenderer.RenderSvg("""
quadrantChart
title Priority Matrix
x-axis Low Effort --> High Effort
y-axis Low Impact --> High Impact
quadrant-1 Do First
quadrant-2 Schedule
quadrant-3 Delegate
quadrant-4 Eliminate
Feature A: [0.8, 0.9]
Feature B: [0.2, 0.3]
Feature C: [0.6, 0.4]
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/quadrant.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/quadrant.light.svg" alt="Quadrant Chart" /></picture></p>
Timeline
MermaidRenderer.RenderSvg("""
timeline
title History of Social Media
section Early Days
2002 : LinkedIn
2004 : Facebook : Google
section Modern Era
2010 : Instagram
2019 : TikTok
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/timeline.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/timeline.light.svg" alt="Timeline" /></picture></p>
GitGraph
MermaidRenderer.RenderSvg("""
gitGraph
commit id: "init"
commit id: "feat-1"
branch develop
checkout develop
commit id: "dev-1"
commit id: "dev-2" tag: "v0.1"
checkout main
merge develop id: "merge-1"
commit id: "release" type: HIGHLIGHT tag: "v1.0"
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/gitgraph.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/gitgraph.light.svg" alt="GitGraph" /></picture></p>
Radar Chart
MermaidRenderer.RenderSvg("""
radar-beta
title Skills Assessment
axis Design, Frontend, Backend, DevOps, Testing
curve c1["Team A"]{4, 3, 5, 2, 4}
curve c2["Team B"]{3, 5, 2, 4, 3}
max 5
graticule polygon
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/radar.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/radar.light.svg" alt="Radar Chart" /></picture></p>
Treemap
MermaidRenderer.RenderSvg("""
treemap-beta
"Engineering": 50
"Marketing": 25
"Sales": 15
"Support": 10
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/treemap.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/treemap.light.svg" alt="Treemap" /></picture></p>
Venn Diagram
MermaidRenderer.RenderSvg("""
venn-beta
set A["Frontend"]
set B["Backend"]
set C["DevOps"]
union A, B["Full Stack"]
union B, C["SRE"]
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/venn.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/venn.light.svg" alt="Venn Diagram" /></picture></p>
Mindmap
MermaidRenderer.RenderSvg("""
mindmap
((Project))
(Planning)
Requirements
Timeline
[Development]
Frontend
Backend
{{Testing}}
Unit Tests
Integration
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/mindmap.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/mindmap.light.svg" alt="Mindmap" /></picture></p>
Gantt
MermaidRenderer.RenderSvg("""
gantt
title Shipping this file
dateFormat YYYY-MM-DD
section Render
Spike the renderer :done, a1, 2026-07-07, 1d
Print this page :active, a2, after a1, 1d
section Polish
Update tests :crit, after a2, 12h
Update docs : 6h
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/gantt.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/gantt.light.svg" alt="Gantt" /></picture></p>
User Journey
MermaidRenderer.RenderSvg("""
journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/journey.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/journey.light.svg" alt="User Journey" /></picture></p>
C4 Architecture
MermaidRenderer.RenderSvg("""
C4Context
title System Context diagram for Internet Banking System
Person(customer, "Banking Customer", "A customer of the bank.")
System(banking, "Internet Banking System", "View accounts and make payments.")
System_Ext(mail, "E-mail System", "Microsoft Exchange")
Rel(customer, banking, "Uses")
Rel(banking, mail, "Sends e-mails", "SMTP")
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/c4.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/c4.light.svg" alt="C4 Architecture" /></picture></p>
Supports Rel, BiRel, Rel_Back (arrow reversed vs argument order), and RelIndex. Directional forms (Rel_U / Rel_D / Rel_L / Rel_R and aliases) parse as plain Rel; layout direction hints are ignored in v1.
Sankey Diagram
MermaidRenderer.RenderSvg("""
sankey-beta
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,Industry,342.165
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/sankey.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/sankey.light.svg" alt="Sankey Diagram" /></picture></p>
XY Chart
MermaidRenderer.RenderSvg("""
xychart-beta
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun]
y-axis "Revenue (in $)" 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500]
line [5000, 6000, 7500, 8200, 9500, 10500]
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/xychart.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/xychart.light.svg" alt="XY Chart" /></picture></p>
Requirement Diagram
MermaidRenderer.RenderSvg("""
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/requirement.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/requirement.light.svg" alt="Requirement Diagram" /></picture></p>
Packet Diagram
MermaidRenderer.RenderSvg("""
packet-beta
title UDP Header
0-15: "Source Port"
16-31: "Destination Port"
32-47: "Length"
48-63: "Checksum"
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/packet.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/packet.light.svg" alt="Packet Diagram" /></picture></p>
Supports range fields (0-15: "Label"), single-bit fields (106: "URG"), and bit-count form (+16: "Source Port").
Kanban
MermaidRenderer.RenderSvg("""
kanban
Todo
Task1
Task2
In Progress
Task3
Done
Task4
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/kanban.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/kanban.light.svg" alt="Kanban" /></picture></p>
Architecture
MermaidRenderer.RenderSvg("""
architecture-beta
group k8s(cloud)[k8s]
group ech(cloud)[ECH]
service edot(server)[EDOT] in k8s
service oteldemo(server)[OtelDemo] in k8s
service es(elastic:elasticsearch)[Elasticsearch] in ech
service kbn(elastic:kibana)[Kibana] in ech
service apm(elastic:apm)[APM] in ech
junction otlp
edot:L -- R:otlp
otlp:L -- T:apm
oteldemo:L -- R:edot
kbn:L -- T:es
apm:L -- R:es
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/architecture.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/architecture.light.svg" alt="Architecture" /></picture></p>
Built-in icons
Icons resolve through Mermaider.Icons.IconRegistry, referenced in a diagram as service id(iconName)[Title].
| Pack | Icons |
|---|---|
| Default (no prefix) | cloud, database, disk, internet, server, generic |
aws: / azure: / gcp: |
compute, storage, database, networking, serverless, load-balancer, queue, cdn, cache |
elastic: |
elasticsearch, kibana, logstash, beats, fleet, serverless, apm, security, observability |
ext: (vendor-neutral) |
waf, api-gateway, k8s, pod, pool, reverse-proxy, web, api, load-balancer, queue, cdn, cache |
The vendor and ext: icons are original, simplified pictograms, not the vendors'
official trademarked artwork. AWS/Azure/GCP's real icon sets are licensed for use in your own
diagrams, not for bundling into a redistributable library, which is why these are bespoke
shapes; register the real logos yourself (see below) if you have the rights to use them.
They render with a colored gradient badge behind them (vendor hue for
aws:/azure:/gcp:/elastic:, neutral slate for ext:). Default-pack icons render plainly
on the themed node box instead.
Adding your own icons
Register any SVG under a name of your choosing, including bare names or pack:icon style
names to group related icons:
using Mermaider.Icons;
IconRegistry.Register("mycompany:widget", """
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="8" fill="#0f62fe"/>
</svg>
""");
architecture-beta
service w(mycompany:widget)[Widget]
Register also has ReadOnlySpan<byte> and Stream overloads for loading icons from disk or
embedded resources without decoding them yourself:
IconRegistry.Register("mycompany:logo", File.ReadAllBytes("logo.svg"));
using var stream = typeof(Program).Assembly.GetManifestResourceStream("MyApp.Icons.logo.svg")!;
IconRegistry.Register("mycompany:logo", stream);
Regardless of which overload you use, every registered icon is stored as sanitized SVG text and
rendered the same way: as a base64 data URL on an <image> element
(<image href="data:image/svg+xml;base64,...">), sized and centered in the service box.
Registration validates and sanitizes the SVG using the same allowlist as
SvgSanitizer: <script> tags, event-handler attributes, and any
href other than a validated base64 data:image/svg+xml/data:image/png URI are rejected outright
(MermaidSvgException), not silently stripped. Custom icons render inside the plain themed node
box, same as the default pack. The colored gradient badge is only applied to the built-in
vendor/ext: icons. If you have the rights to use a vendor's real logo (e.g. inside your own
company's tooling), register it under whatever name you like and it renders exactly as provided.
Block Diagram
MermaidRenderer.RenderSvg("""
block-beta
columns 3
A["A"] B["B"] C["C"]
D["D"] E["E"] F["F"]
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/block.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/block.light.svg" alt="Block Diagram" /></picture></p>
TreeView
MermaidRenderer.RenderSvg("""
treeView-beta
my-project/
src/
index.ts :::highlight ## entry point
utils.ts
tests/
index.test.ts
package.json
README.md
""");
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/treeview.dark.svg" /><img src="https://raw.githubusercontent.com/nullean/mermaider/main/.github/readme/treeview.light.svg" alt="TreeView" /></picture></p>
Supports indentation-based and box-drawing (├──/└──/│) input formats. Annotations:
:::className (highlighting), ## description (inline notes), icon(name) (custom icons).
Built-in icons: file, folder, folder-open, file:code, file:image, file:document,
file:config, file:data.
Attribution
This project started as a .NET port of beautiful-mermaid by Craft Docs (lukilabs). Their TypeScript library pioneered the idea of rendering Mermaid diagrams without a browser or DOM: fast, themeable, and synchronous.
beautiful-mermaid itself credits mermaid-ascii by
Alexander Grooff for its ASCII rendering engine, which was ported from Go to TypeScript and extended.
beautiful-mermaid, relies on an external battle hardened layout engine elk.js,
We owe a huge thank-you to both projects for the excellent foundation.
A note on how this was built
This codebase was written with a coding agent (Claude). That said, care was taken to follow modern .NET 10
idioms and keep allocations low: ReadOnlySpan<char> parsing, [GeneratedRegex] with ReDoS timeout guards,
FrozenDictionary / FrozenSet for hot-path lookups, SearchValues<char> for character classification,
object pooling, and file-scoped namespaces throughout. The benchmark numbers above reflect the result.
Projects using Mermaider
Projects that use Mermaider and have contributed back:
- elastic/docs-builder - Elastic's documentation build toolchain
- tig/winprint - WinPrint uses Mermaider as its default Mermaid renderer
License
MIT. See LICENSE.txt.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.ObjectPool (>= 10.0.3)
- Sugiyama (>= 0.15.1)
NuGet packages (13)
Showing the top 5 NuGet packages that depend on Mermaider:
| Package | Downloads |
|---|---|
|
LiveMarkdown.Avalonia.Mermaid
This is a extension package for LiveMarkdown.Avalonia that adds support for rendering Mermaid diagrams. |
|
|
CodeWF.Markdown
CodeWF.Markdown 完整包:在 CodeWF.Markdown.Lite 渲染引擎之上提供图片(GIF/SVG/预览)、数学公式、Mermaid 图表、TextMate 代码高亮、PNG/PDF/Word/公众号 HTML 导出,以及 Markdown 源码编辑器与单栏实时编辑(所见即所得)控件。Full CodeWF.Markdown package: rendering capabilities plus source and WYSIWYG editors. |
|
|
CodeWF.Markdown.Themes
CodeWF.Markdown 完整包的样式入口:在 CodeWF.Markdown.Lite.Themes 的控件模板与 18 套排版主题之上,叠加图片能力控件外观。Styles entry point for the full CodeWF.Markdown package. |
|
|
Mermaider.Layout.Msagl
Optional, legacy-compatible Microsoft MSAGL layout provider for Mermaider. The built-in layout engine is faster and supports compound subgraph layout and orthogonal routing; use this package to keep output from earlier versions. |
|
|
Jumbee.Console.Documents
Document viewers and editors for Jumbee.Console: Markdown, AsciiDoc, and Mermaid diagram rendering and interactive editing controls for TUIs. |
GitHub repositories (2)
Showing the top 2 popular GitHub repositories that depend on Mermaider:
| Repository | Stars |
|---|---|
|
DearVa/LiveMarkdown.Avalonia
High performance, real-time markdown renderer for AI/LLM
|
|
|
tig/winprint
Human and AI Agent friendly print utility with syntax highlighting, markdown support, multiple pages-up, headers/footers. Cross platform (Mac, Linux, Windows) GUI and TUI. Print source code, web pages, and reports generated by legacy systems.
|
| Version | Downloads | Last Updated |
|---|---|---|
| 0.15.1 | 1,723 | 10/6/2026 |
| 0.15.0 | 93 | 10/6/2026 |
| 0.14.1 | 561 | 10/5/2026 |
| 0.14.0 | 173 | 10/5/2026 |
| 0.13.1 | 4,643 | 9/11/2026 |
| 0.13.0 | 148 | 9/11/2026 |
| 0.12.2 | 18,113 | 8/11/2026 |
| 0.12.1 | 7,760 | 7/22/2026 |
| 0.12.0 | 169 | 7/22/2026 |
| 0.11.1 | 179 | 7/22/2026 |
| 0.11.0 | 285 | 7/21/2026 |
| 0.10.1 | 153 | 7/21/2026 |
| 0.10.0 | 140 | 7/21/2026 |
| 0.9.0 | 306 | 7/14/2026 |
| 0.8.0 | 8,754 | 6/2/2026 |
| 0.7.1 | 281 | 5/20/2026 |
| 0.7.0 | 134 | 5/20/2026 |
| 0.6.0 | 3,771 | 3/4/2026 |
| 0.5.0 | 346 | 2/27/2026 |