MermaidKit

Native Mermaid diagrams in Swift.

All 30 Mermaid diagram types, parsed, laid out, and rendered in pure Swift — natively on macOS/iOS, Linux, Android, Windows, WebAssembly, and Flutter, plus any bare framebuffer or SDL surface, with byte-identical output.

No JavaScriptNo WebViewZero dependencies
MermaidKit's own render pipeline drawn as a sankey diagram, rendered by MermaidKit
One line to a diagram

Drop it into SwiftUI.

A MermaidView follows light/dark automatically, sizes to the diagram, caches renders, and describes itself to VoiceOver. That's the whole integration.

Install
.package(
  url: "https://github.com/2389-research/MermaidKit.git",
  from: "2.2.0"
)
import MermaidRender

MermaidView("""
flowchart TD
    A[Start] --> B{Choice}
    B -->|yes| C[Do it]
    B -->|no| D[Skip]
""")
New in 2.0 — native everywhere

One Swift core. Six native platforms. Byte-identical output.

The platform-free layout core cross-compiles to every target; a fully-resolved scene crosses the C ABI as a language-neutral JSON contract (SceneWire), and each platform paints it with a thin native renderer. The scene's output is proven byte-identical across the five it compiles to on every push — and a raw raster drives any bare surface beyond them.

Apple — CoreGraphics

macOS, iOS, visionOS. MermaidView and the MermaidRenderer image/PDF/SVG API, drawn with CoreGraphics/CoreText.

Linux — Silica

Headless Cairo/FontConfig rendering behind the LinuxRaster trait; the platform-free layout runs anywhere Swift does.

Android — Kotlin Canvas

MermaidDiagram("…", Modifier.fillMaxWidth()) — one line, themed from MaterialTheme, device-font measured, contentDescription from the narration. JNI over a per-ABI .so, shipped as an AAR.

Windows — .NET / SkiaSharp

MermaidNative.Scene("…")SceneRenderer.Draw(canvas). Real Skia — the same engine as Android — over a P/Invoked Swift DLL. No SVG fallback.

Flutter — Dart CustomPainter

MermaidNative.scene("…")MermaidPainter on Flutter's Skia canvas, over dart:ffi. One plugin reaches iOS, Android, web, and desktop.

WebAssembly

The core compiles to wasm32-unknown-wasi and emits SVG (or a Canvas2D scene) in the browser — the layout engine, running client-side.

Any bare surface

A raw RGBA raster with no display server drives a Raspberry Pi framebuffer (/dev/fb0), an SDL2 window, or a GPU texture — see tools/pi-canvas, an infinite pannable canvas.

Proven identical

A conformance harness runs the same fixtures on every platform and asserts one signature. It caught two real cross-platform determinism bugs — and now gates them forever.

It checks its own work

Layout quality is machine-checked, not eyeballed.

Every diagram lowers to a machine-readable geometry scene — boxes, edge routes, label frames. A geometry linter checks the invariants of good layout on every CI run and reports each failure as a named geometric fact, with coordinates.

mermaidkit-lint · fixtures/
$ swift run mermaidkit-lint fixtures/
error edge-through-node edge #3 passes through node "DiagramScene" (165pt inside)
error label-collision label "network-simplex" sliced by edge #7 (12pt overlap)
error node-overlap "Parse""Layout" frames intersect (18×9pt)
error off-canvas node "Cache" outside drawing (x = -14pt)
warn series-escape series "cold render" leaves plot (y=262 > 250)
warn t-junction junction at (410,208) counted as a crossing
ok ✓ 30/30 fixtures lint clean · draw-vs-scene conformance 100%
  • edge-through-node — a connector routed straight through the interior of a box instead of around it.
  • label-collision — a line crossing over label text, slicing the words so they can't be read.
  • node-overlap — two node frames occupying the same space, one drawn on top of the other.
  • off-canvas — any node, edge, or label drawn outside the diagram's own bounds.
  • series-escape — a chart series (line, bar, point) leaving the plot area it should stay within.
  • t-junction — a T-shaped meeting of edges miscounted as a genuine crossing.

Why this is a big deal

A named fact, not a blob.

A regression fails CI as "edge #3 is 165pt inside node DiagramScene" — a coordinate you can act on, not a pixel-diff image or a human squinting at a screenshot.

The pictures are the tests.

The same 30 fixtures are the gallery, the lint corpus, and the benchmark suite. The examples on this page cannot drift from what the engine actually does.

It runs on every build.

Geometry invariants run in CI, so a layout regression can't merge. Most tools rely on eyeballing or pixel snapshots that flake across fonts and OSes — checking geometry is stable, precise, and catches the real defect.

Layout is engineered underneath: network-simplex layer assignment (the strategy Graphviz dot defaults to), label-space reservation during layout, chain straightening after coordinate assignment, and edit-stability proven by scene-diff tests — rename a label to same-width text and nothing else moves.

One call, one image — or pure geometry

An API that meets you where you are.

SwiftUI drop-in

MermaidView(source, theme:spacing:) — light/dark aware, self-sizing, cached, VoiceOver-described.

Image, PDF, attributed string

MermaidRenderer.image / pdfData / attachmentString — same layout and draw code; vector PDF for crisp export and print.

Standalone SVG, no CoreGraphics

MermaidRenderer.svg(source:theme:) exports a standalone SVG document for all 30 types. It lowers through RenderScene — a Codable, platform-free render IR — so the SVG backend runs headless on Linux and in CI, and, projected to the SceneWire JSON contract, is the foundation for the shipped native Android and Windows renderers.

Accessibility from day one

altText(source:) generates a deterministic content description for every one of the 30 types; MermaidAltText.narrate(_:) adds a step-by-step walkthrough — decisions, states, ER cardinalities, sequence messages.

Themed with a single value

DiagramTheme — six colors plus a categorical palette re-skin all 30 types at once; DiagramSpacing sets density.

Diagnose parse failures

MermaidParser.diagnose(_:) explains failures with line numbers and did-you-mean suggestions for typo'd headers.

Headless geometry

The parse → layout → scene pipeline is platform-free — no AppKit/UIKit. Tools get frames, polylines, and label boxes without rendering a pixel.

More than Mermaid in

DOTParser, DippinParser, SQLDDLParser, and GitLogParser parse Graphviz DOT, Dippin, SQL CREATE TABLE, and raw git log output into the same engine; DOTExporter emits DOT back — a Mermaid ⇄ DOT converter. SQL DDL renders as an ER diagram with PK/FK/UK badges; git log renders as a gitgraph.

Renders in the terminal

The mermaidkit-term CLI draws any Mermaid/DOT/Dippin source in the terminal — Kitty graphics, half-block truecolor, colored box-drawing, or plain ASCII. Platform-free; runs headless on Linux/CI.

Numbers over adjectives

What it costs, plainly.

30
diagram types, natively
5
input front-ends: Mermaid, DOT, Dippin, SQL DDL, git log
0
third-party deps by default (Silica-free), JS, or WebView
~25 ms
worst cold render incl. rasterization (sankey); most types under ~15 ms
398
tests in CI on macOS (380 on Linux)
6
native platforms: macOS/iOS, Linux, Android, Windows, WASM, Flutter (+ framebuffer/SDL)
MIT
license

Repeat renders hit an in-memory cache keyed by (source, theme, spacing). Input caps mirror mermaid.js — 50,000-character sources, 500 flowchart edges; oversized input returns nil fast. Requires Swift 6.2 / Xcode 26. Platforms: macOS 14+, iOS 17+, visionOS 1+, Linux (via the LinuxRaster package trait), and — via the platform-free scene core — Android, Windows/.NET, Flutter, and WebAssembly, plus any bare framebuffer or SDL/GPU surface.

Where the time goes: drawing (rasterization) is ~75% of a cold render; the parse-and-layout plumbing is lean, with a ~0.2 ms fixed floor. It's profiled, not guessed — the one "obvious" win, an 8-bit sRGB backing store, measured ~2× slower on Apple silicon, so it stayed f16. Full performance analysis →

Built to adapt

It behaves well when the input doesn't.

Nothing breaks the host app

Unknown dialects degrade to readable monospaced source. Styling directives are ignored, not fatal. Garbage, 100k-character labels, and NaN/Infinity are contained by an adversarial test suite with numeric sanitation at the parser boundary.

What you wrote is what parses

A dedicated "parser honesty" effort eliminated silent content loss and phantom content across the parsers — each fix pinned by a regression test using the exact previously-broken syntax.

One core, six platforms

The same platform-free layout core renders through native backends: CoreGraphics on Apple, Silica/Cairo on Linux, Kotlin Canvas on Android, SkiaSharp on Windows/.NET, a Dart CustomPainter on Flutter, and SVG/Canvas2D on WebAssembly — with the scene's output proven byte-identical across the five it compiles to in CI, and a raw raster for any bare framebuffer/SDL surface.

Honest support matrix

The README states plainly what parses, what's ignored, and what's a known gap worth filing. SVG export now ships for all 30 types; lower OS floors sit at the top of the roadmap.

Read the docs

Documented as carefully as it's built.

Guides, deep-dive engineering notes, and the full history — all on GitHub.

Getting StartedRender your first diagram in one line, then pick the right API for your app. Headless LayoutParse → layout → scene geometry with no rendering — frames, polylines, label boxes. Scene Geometry & the LinterHow every diagram lowers to a checkable scene, and the invariants CI enforces. ThemingRe-skin all 30 diagram types at once with a single DiagramTheme value. Embedding in Text ViewsDrop a diagram into an NSAttributedString, VoiceOver description included. Adding a Diagram TypeThe three small pieces — parser, layout, renderer — behind every type. Linux Rendering via SilicaHow the one CoreGraphics seam maps to Cairo/FontConfig for headless Linux output. Cross-platform ConformanceProving the core's output is byte-identical on macOS, Linux, Android, WASM, and Windows — and the two determinism bugs it caught. MermaidKit on AndroidThe Kotlin/Canvas renderer, the JNI seam, Material theming, and the AAR — source string to a themed, accessible diagram. MermaidKit on WindowsThe .NET/SkiaSharp renderer and the P/Invoke bridge — the same scene, a native Skia surface, no SVG fallback. MermaidKit on FlutterA Dart CustomPainter over the SceneWire scene, a dart:ffi source bridge — one plugin for iOS, Android, web, and desktop. Bare surfaces — Pi & SDLAn infinite pannable canvas composited into a Raspberry Pi framebuffer or an SDL2 window, over the platform-free raw raster. Mermaid Coverage AuditWhat each type parses, what's ignored, and where the known gaps are. PerformanceMethodology, per-type numbers, where the time goes, and the fixable-vs-irreducible costs. Terminal RenderingBeyond ASCII — the Kitty-graphics → half-block → box-drawing capability ladder, and how each tier degrades. IR Compilation TargetsCompiling the scene IR to targets beyond images — SVG, DOT, and other export back-ends. Runtime PluginsHow a versioned IR/scene JSON contract could let people add front-ends and back-ends at runtime, in any language. Sequence PrimitivesThe maximal set of sequence-diagram primitives, surveyed across mermaid.js, PlantUML, and ZenUML. How It's BuiltThe practices behind the project — strong boundaries, tests that encode taste, and verifying against reality. ContributingThe lay of the land, the geometry-first review bar, and developing on Linux. ChangelogEvery release through 2.1 — native Android/Windows/Flutter/WASM rendering, bare framebuffer & SDL surfaces, cross-platform conformance, SVG export — with the reasoning behind each change.