Skip to content

nullean/mermaider

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mermaider

Mermaider

Render Mermaid diagrams to SVG in pure .NET.
No browser. No DOM. No JavaScript runtime. AOT-ready.

NuGet CI


Table of Contents


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 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

Graph-based diagrams (flowchart, state, class, ER) need a layout algorithm to position nodes and route edges. Other diagram types (pie, quadrant, timeline, gitgraph, radar, treemap, venn, mindmap, gantt, journey, C4, sankey, xychart, requirement, packet, kanban, architecture, block, treeview) use purpose-built layout arithmetic directly in their renderers. Rather than depending on an external engine, Mermaider ships its own lightweight Sugiyama layout engine with zero dependencies.

During development, Microsoft MSAGL (Automatic Graph Layout) was evaluated as the layout backend. MSAGL is a capable research-grade library, but it carries baggage from a different era of .NET: high allocations (~554 KB for a 6-node flowchart), WPF-era BinaryFormatter usage, and trim/AOT warnings that make it unsuitable for modern deployment targets.

The built-in engine is purpose-built for the small-to-medium directed graphs Mermaid produces:

Phase MSAGL Built-in Sugiyama Improvement
Layout only 247 µs / 558 KB 3.4 µs / 16 KB 73× faster, 35× less memory
End-to-end render 351 µs / 586 KB 24 µs / 46 KB 15× faster, 13× less memory

If you still want MSAGL for its higher-fidelity edge routing on complex graphs, install the optional Mermaider.Layout.Msagl package (see below).

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 href URIs are structurally excluded; the only permitted href is a base64 data:image/svg+xml or data:image/png on 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
  • Typography: Font, MonoFont, FontSize and 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.

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? derived Edge/connector stroke color
Accent string? derived Arrowheads, highlights
Muted string? derived Secondary text, edge labels
Surface string? derived Node fill tint
Border string? derived Node/group stroke
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
AllowedDiagrams DiagramTypes DiagramTypes.All Allowlist of accepted diagram types; diagrams outside this set throw MermaidParseException
RoundedEdges bool true Rounded corners (6px radius) on edge paths
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, and linkStyle directives (diagram renders; violations reported via OnStripped)
  • Strips source-authored theme / themeVariables overrides 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, return MermaidRenderer.FallbackSvg, the canonical empty SVG document.
  • SanitizeMode.Block: throw MermaidSvgException with 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.

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 --list-themes

MSAGL Layout Provider

If you prefer MSAGL for its edge routing fidelity on complex graphs, install the optional package:

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 Pro):

Method Mean Allocated
Flowchart (simple) ~23 µs ~46 KB
Flowchart (large) ~71 µs ~145 KB
Sequence ~12 µs ~28 KB
State ~17 µs ~47 KB
Class ~13 µs ~36 KB
ER ~17 µs ~45 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.

Mermaider playground - all diagram types with theme controls

Flowchart

MermaidRenderer.RenderSvg("""
    graph TD
      A[Start] --> B{Decision}
      B -->|Yes| C[OK]
      B -->|No| D[Cancel]
      C --> E[End]
      D --> E
    """);

Flowchart

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!
    """);

Sequence diagram

State

MermaidRenderer.RenderSvg("""
    stateDiagram-v2
      [*] --> Idle
      Idle --> Processing : submit
      Processing --> Success : ok
      Processing --> Failed : error
      Success --> [*]
      Failed --> Idle : retry
    """);

State diagram

Class

MermaidRenderer.RenderSvg("""
    classDiagram
      class Animal {
        <<abstract>>
        +String name
        +eat() void
      }
      class Dog { +bark() void }
      class Cat { +purr() void }
      Animal <|-- Dog
      Animal <|-- Cat
    """);

Class diagram

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
      }
    """);

ER diagram

Pie Chart

MermaidRenderer.RenderSvg("""
    pie
    title Pet Adoption
    "Dogs" : 386
    "Cats" : 85
    "Rats" : 15
    """);

Pie chart

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]
    """);

Quadrant chart

Timeline

MermaidRenderer.RenderSvg("""
    timeline
    title History of Social Media
    section Early Days
    2002 : LinkedIn
    2004 : Facebook : Google
    section Modern Era
    2010 : Instagram
    2019 : TikTok
    """);

Timeline diagram

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"
    """);

GitGraph

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
    """);

Radar chart

Treemap

MermaidRenderer.RenderSvg("""
    treemap-beta
    "Engineering": 50
    "Marketing": 25
    "Sales": 15
    "Support": 10
    """);

Treemap

Venn Diagram

MermaidRenderer.RenderSvg("""
    venn-beta
    set A["Frontend"]
    set B["Backend"]
    set C["DevOps"]
    union A, B["Full Stack"]
    union B, C["SRE"]
    """);

Venn diagram

Mindmap

MermaidRenderer.RenderSvg("""
    mindmap
      ((Project))
        (Planning)
          Requirements
          Timeline
        [Development]
          Frontend
          Backend
        {{Testing}}
          Unit Tests
          Integration
    """);

Mindmap

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
    """);

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
    """);

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")
    """);

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.

C4 diagram

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
    """);

Sankey diagram

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]
    """);

XY chart

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
    """);

Requirement diagram

Packet Diagram

MermaidRenderer.RenderSvg("""
    packet-beta
    title UDP Header
    0-15: "Source Port"
    16-31: "Destination Port"
    32-47: "Length"
    48-63: "Checksum"
    """);

Supports range fields (0-15: "Label"), single-bit fields (106: "URG"), and bit-count form (+16: "Source Port").

Packet diagram

Kanban

MermaidRenderer.RenderSvg("""
    kanban
      Todo
        Task1
        Task2
      In Progress
        Task3
      Done
        Task4
    """);

Kanban board

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
    """);

Architecture diagram

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"]
    """);

Block diagram

TreeView

MermaidRenderer.RenderSvg("""
    treeView-beta
        my-project/
            src/
                index.ts :::highlight ## entry point
                utils.ts
            tests/
                index.test.ts
            package.json
            README.md
    """);

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.

Tree view diagram


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:

License

MIT. See LICENSE.txt.

About

A pure dotnet mermaid parser, layout engine AND renderer, no js runtime, AOT ready.

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages