Skip to content

Papyrine/Naiad

Repository files navigation

Naiad

Build status NuGet Status

A .NET library for rendering Mermaid diagrams to SVG. No browser or JavaScript runtime required.

Try it in a browser ↗ — a live Mermaid editor (in the spirit of mermaid.live) rendered entirely client-side by Naiad on Blazor WebAssembly.

PNG output is available via two optional companion packages — Naiad.Skia (SkiaSharp) and Naiad.ImageSharp (SixLabors.ImageSharp).

Open Source Maintenance Fee

This project participates in the Open Source Maintenance Fee. The source code is freely available under the terms of the license. To support sustainable maintenance, use of the project's official binary releases in revenue-generating activities and all government agencies requires adherence to the Open Source Maintenance Fee EULA. The fee is paid by sponsoring Papyrine.

This project uses SponsorCheck to surface a build-time reminder in consuming projects that are not yet sponsoring.

NuGet package

https://nuget.org/packages/Naiad

Usage

var svg = Mermaid.Render(
    """
    flowchart LR
        A[Start] --> B[Process] --> C[End]
    """);

snippet source | anchor

The diagram type is automatically detected from the input.

Render Options

var svg = Mermaid.Render(
    input,
    new()
    {
        Padding = 20,
        FontSize = 14,
        FontFamily = "Arial, sans-serif"
    });

snippet source | anchor

PNG output

The core Naiad package renders SVG only and has no third-party dependencies. To rasterize a diagram to PNG, add one of the two backend packages. Both drive the exact same parse → layout → style pipeline that produces the SVG, and share all of the SVG-to-pixels code; they differ only in the rasterizer and font engine they use.

Package Rasterizer When to choose
Naiad.Skia SkiaSharp Native Skia rendering; bundled native binaries.
Naiad.ImageSharp SixLabors.ImageSharp Fully managed, cross-platform; uses installed system fonts for text. Depends on ImageSharp under the Six Labors Split License.

// Naiad.Skia
var skiaPng = SkiaRenderer.RenderPng(input);
SkiaRenderer.RenderPng(input, "diagram.png");

// Naiad.ImageSharp
var imageSharpPng = ImageSharpRenderer.RenderPng(input);
ImageSharpRenderer.RenderPng(input, "diagram.png");

snippet source | anchor

Both renderers accept the same RenderOptions as Mermaid.Render, plus a Png section controlling rasterization:

SkiaRenderer.RenderPng(
    input,
    "diagram.png",
    new()
    {
        Png =
        {
            // 2x device-pixel scale for high-DPI output
            Scale = 2,
            // any CSS colour, or "transparent"
            Background = "white",
            // trade encode speed against file size: Fast, Balanced (default) or Small
            Compression = PngCompression.Balanced
        }
    });

snippet source | anchor

RenderPng also has an overload that writes to a Stream.

Compression trades PNG encode speed against file size; the rendered pixels are identical at every level. Fast (zlib level 1) encodes much faster but produces larger files, Small (level 9) the opposite, and Balanced (the default, level 6) sits between. Encoding is a sizeable share of render time — especially on the ImageSharp backend — so Fast is worth choosing when throughput matters more than file size.

AllowHtmlElements

By default, node and edge labels are emitted as HTML inside <foreignObject> (matching Mermaid's own output). Set AllowHtmlElements = false to render labels as native SVG <text> instead, producing self-contained SVG for viewers and rasterizers that have no HTML/CSS engine. Icons that rely on the HTML path — FontAwesome glyphs and icons embedded in labels — are omitted in this mode; iconify pack icons drawn as inline SVG are unaffected.

For example, this single-node graph:

var svg = Mermaid.Render(
    """
    flowchart LR
        A[Hello]
    """);

By default the label is a <foreignObject>, and a Font Awesome @import is included:

<svg
  id="naiad"
  width="100%"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 108.5 88"
  style="max-width: 108.5px;"
  role="graphics-document document">
  <style
    xmlns="http://www.w3.org/1999/xhtml">@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css");</style>
  <style>
#naiad{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#naiad p{margin:0;}
#naiad .marker{fill:#333333;stroke:#333333;}
#naiad .marker.cross{stroke:#333333;}
#naiad span{fill:#333;color:#333;}
#naiad .flowchart-link{stroke:#333333;fill:none;}
#naiad .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}
#naiad .edgeLabel p{background-color:rgba(232,232,232, 0.8);}
#naiad .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}
</style>
  <g
    transform="translate(20,20)">
    <path
      d="M0,0 H68.5 V48 H0 Z"
      fill="#ECECFF"
      stroke="#9370DB" />
    <foreignObject
      x="0"
      y="0"
      width="68.5"
      height="48"
      class="nodeLabel">
      <div
        xmlns="http://www.w3.org/1999/xhtml"
        style="display: table-cell; white-space: nowrap; text-align: center; vertical-align: middle; width: 68.5px; height: 48px;">
        <span>
          <p>Hello</p>
        </span>
      </div>
    </foreignObject>
  </g>
</svg>

snippet source | anchor

With AllowHtmlElements = false, the label is a native <text> and the Font Awesome @import is gone:

<svg
  id="naiad"
  width="100%"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 108.5 88"
  style="max-width: 108.5px;"
  role="graphics-document document">
  <style>
#naiad{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#naiad p{margin:0;}
#naiad .marker{fill:#333333;stroke:#333333;}
#naiad .marker.cross{stroke:#333333;}
#naiad span{fill:#333;color:#333;}
#naiad .flowchart-link{stroke:#333333;fill:none;}
#naiad .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}
#naiad .edgeLabel p{background-color:rgba(232,232,232, 0.8);}
#naiad .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}
</style>
  <g
    transform="translate(20,20)">
    <path
      d="M0,0 H68.5 V48 H0 Z"
      fill="#ECECFF"
      stroke="#9370DB" />
    <text
      x="34.25"
      y="24"
      text-anchor="middle"
      dominant-baseline="middle"
      font-size="14px"
      font-family="Arial, sans-serif"
      class="nodeLabel">Hello</text>
  </g>
</svg>

snippet source | anchor

Icon packs

Naiad can render icons from iconify icon packs. Packs are not bundled — load the ones you need (in the iconify JSON format) from a file or a stream:

IconPack.Load("logos.json");

// ...or from a stream
using var stream = File.OpenRead("logos.json");
IconPack.Load(stream);

snippet source | anchor

Pack files are published as @iconify-json/* packages (the icons.json file), e.g. @iconify-json/logos. Load registers the pack under its prefix and returns it. Register all packs once at startup — calling IconPack.Load after the first Mermaid.Render throws a MermaidException.

Once loaded, reference icons as prefix:name wherever a diagram supports icons — architecture services and groups, flowchart node labels, and mindmap nodes:

// Architecture
Mermaid.Render(
    """
    architecture-beta
    service fn(logos:aws-lambda)[Lambda]
    service db(logos:postgresql)[Database]
    fn:R -- L:db
    """);

// Flowchart (inline in labels)
Mermaid.Render(
    """
    flowchart LR
        A[logos:redis Cache] --> B[logos:postgresql DB]
    """);

// Mindmap
Mermaid.Render(
    """
    mindmap
      Project
        Storage ::icon(logos:aws-s3)
    """);

snippet source | anchor

Single-color icons (e.g. mdi, tabler) inherit the surrounding color; multi-color icons (e.g. logos) keep their own palette.

Icons also rasterize to PNG. Iconify pack icons are inline SVG, so they render identically in SVG and PNG. FontAwesome icons (fa:fa-name in flowcharts, ::icon(fa fa-name) in mindmaps) resolve through the FontAwesome webfont in SVG without a pack; PNG has no webfont, so a fa: icon is drawn there only when a pack registered under the fa prefix supplies its geometry (an icon keyed fa-car, fa-bell, …), and is otherwise omitted:

// Load packs up front, before the first render.
IconPack.Load("logos.json");
IconPack.Load("fontawesome.json");

// Iconify pack icons are inline SVG, so they rasterize to PNG just like the rest of the diagram.
var withIconifyIcon = SkiaRenderer.RenderPng(
    """
    flowchart LR
        A[logos:redis Cache] --> B[logos:postgresql DB]
    """);

// FontAwesome glyphs have no geometry of their own, so a fa: icon only appears in PNG when a
// pack registered under the "fa" prefix supplies it (icons keyed fa-bell, fa-car, ...).
var withFontAwesomeIcon = SkiaRenderer.RenderPng(
    """
    flowchart LR
        A[fa:fa-bell Alerts]
    """);

snippet source | anchor

Supported Diagram Types

Test Renders

Auto-generated documentation from the test suite.

Beta diagram types

Icon

Naiad designed by Icons Producer from The Noun Project.

About

A .NET library for rendering Mermaid diagrams to SVG. No browser or JavaScript runtime required.

Resources

License

Code of conduct

Stars

79 stars

Watchers

2 watching

Forks

Contributors

Languages