10 KiB
@tldraw/mermaid
Convert Mermaid diagram syntax into native, editable tldraw shapes.
Instead of rendering a static SVG, @tldraw/mermaid parses Mermaid text and creates real geo shapes, arrows, frames, and groups on the tldraw canvas — so users can move, resize, restyle, and connect them like any other shape.
Installation
npm i @tldraw/mermaid
Peer dependencies: your app must also include compatible versions of:
tldraw— the SDK this package plugs intoreact—^18.2.0or^19.2.1react-dom—^18.2.0or^19.2.1
Quick start
import { createMermaidDiagram } from '@tldraw/mermaid'
// Inside a component or callback with access to the editor:
await createMermaidDiagram(
editor,
`
flowchart TD
A[Start] --> B{Decision}
B -->|Yes| C[Do something]
B -->|No| D[Do something else]
`
)
By default, shapes are centered on the viewport. You can control placement with blueprintRender:
// Center the diagram on a specific point (default behavior)
await createMermaidDiagram(editor, diagramText, {
blueprintRender: {
position: { x: 500, y: 300 },
centerOnPosition: true,
},
})
// Place the diagram's top-left corner at the given point
await createMermaidDiagram(editor, diagramText, {
blueprintRender: {
position: { x: 0, y: 0 },
centerOnPosition: false,
},
})
When centerOnPosition is true (the default), the diagram's center aligns with the given position. When false, the diagram's top-left corner aligns with it instead. If no position is provided, the diagram uses the viewport center — or the cursor position if the user has "paste at cursor" mode enabled.
Supported diagram types
| Diagram type | Mermaid keyword | What you get |
|---|---|---|
| Flowchart | flowchart, graph |
Geo shapes, arrows, subgraph frames |
| Sequence diagram | sequenceDiagram |
Actor shapes, lifelines, signal arrows, fragment frames, participant boxes |
| State diagram | stateDiagram-v2 |
State shapes, transitions, compound state frames, fork/join, choice |
| Mindmap | mindmap |
Colored geo shapes, parent-child edges, tree hierarchy |
Unsupported diagram types (pie, gantt, class, ER, etc.) can be handled with the onUnsupportedDiagram callback — for example, to fall back to SVG import:
await createMermaidDiagram(editor, text, {
onUnsupportedDiagram(svgString) {
editor.putExternalContent({ type: 'svg-text', text: svgString })
},
})
API
createMermaidDiagram(editor, text, options?)
Parses Mermaid text, extracts layout from the rendered SVG, builds a blueprint, and creates tldraw shapes.
editor— a tldrawEditorinstance.text— Mermaid diagram source text.options— optionalMermaidDiagramOptions:mermaidConfig— Mermaid configuration overrides (theme, spacing, etc.).blueprintRender— positioning (position,centerOnPosition) and optionalmapNodeToRenderSpec(see below).onUnsupportedDiagram(svg)— callback when the diagram type isn't natively supported.
Throws MermaidDiagramError on parse failure or unsupported diagram type (if no callback is provided).
renderBlueprint(editor, blueprint, opts?)
Renders a pre-built DiagramMermaidBlueprint into the editor. Useful if you want to construct or modify a blueprint programmatically before rendering. Options include mapNodeToRenderSpec to customize how each node is materialized (see below).
MermaidDiagramError
Error class with type: 'parse' | 'unsupported' and diagramType properties.
Customizing node shapes
Blueprint nodes carry layout and semantic kind only. At renderBlueprint time, each node is mapped to a MermaidBlueprintNodeRenderSpec: either { variant: 'geo', geo } (defaults use the same built-in tables as before) or { variant: 'shape', type, props } for any shape type registered on your editor.
Pass mapNodeToRenderSpec on blueprintRender (or to renderBlueprint directly) with a callback (input) => spec | undefined. input includes diagramKind, nodeId, kind, and the full node. Return undefined to keep the package default for that node. defaultCreateMermaidNodeFromBlueprint then merges the spec with layout (size, colors, label text) and calls editor.createShape once per node.
Custom type values must exist on the editor before import. Exotic shapes may need compatible geometry for arrow bindings to feel like built-in geo nodes.
Helpers: defaultMermaidNodeRenderSpec, resolveMermaidNodeRender, MERMAID_MINDMAP_NODE_TYPE (mindmap kind strings match String of these integers).
Types
DiagramMermaidBlueprint—{ diagramKind, nodes, edges, lines?, groups? }.MermaidBlueprintNode— layout, semantickind, and style fields (materialization is chosen at render time).MermaidBlueprintNodeRenderSpec—variant: 'geo'+geo, orvariant: 'shape'+type+props.MermaidDiagramKind—'flowchart' | 'state' | 'sequence' | 'mindmap'.MermaidBlueprintEdge— an arrow with start/end node IDs, bend, arrowheads, dash style, and anchor positions.MermaidBlueprintLineNode— a vertical or horizontal line (used for sequence diagram lifelines).BlueprintRenderingOptions— position, centering, and optionalmapNodeToRenderSpecforrenderBlueprint/createMermaidDiagram.MermaidNodeCreateFunctionArgs— argument object fordefaultCreateMermaidNodeFromBlueprint(includes resolvedrenderspec).
How it works
The conversion happens in three stages:
flowchart LR
MermaidText["Mermaid text"] --> ParseRender["Parse and render SVG"]
ParseRender --> ExtractBlueprint["Extract blueprint"]
ExtractBlueprint --> CreateShapes["Create tldraw shapes"]
- Parse and render — Mermaid parses the text and renders an SVG into an offscreen DOM element, giving us accurate layout positions via
getBBox(). - Extract blueprint — a diagram-specific converter reads positions from the SVG and semantic data from Mermaid's internal database, producing a
DiagramMermaidBlueprint. - Create shapes —
renderBlueprintresolves each node’s materialization spec (mapper or default), then creates tldraw shapes (geo or custom), plus arrows, lines, frames, and groups.
Example: paste handler
A common integration is converting Mermaid text on paste. Here's a React component that registers itself as a text content handler — based on the pattern used on tldraw.com:
import { useEffect } from 'react'
import { defaultHandleExternalTextContent, useEditor } from 'tldraw'
const MERMAID_KEYWORD =
/^\s*(flowchart|graph|sequenceDiagram|stateDiagram|classDiagram|erDiagram|gantt|pie|gitGraph|mindmap)/
export function MermaidPasteHandler() {
const editor = useEditor()
useEffect(() => {
editor.registerExternalContentHandler('text', async (content) => {
if (!MERMAID_KEYWORD.test(content.text)) {
await defaultHandleExternalTextContent(editor, content)
return
}
try {
const { createMermaidDiagram } = await import('@tldraw/mermaid')
await createMermaidDiagram(editor, content.text, {
async onUnsupportedDiagram(svgString) {
await editor.putExternalContent({ type: 'svg-text', text: svgString })
},
})
} catch {
await defaultHandleExternalTextContent(editor, content)
}
})
}, [editor])
return null
}
Drop <MermaidPasteHandler /> inside your <Tldraw> component and users can paste Mermaid text directly onto the canvas.
The regex above is a simplified check. On tldraw.com the detection is handled by simpleMermaidStringTest, which also strips YAML frontmatter (---...---), %%{...}%% directives, and %% comments before testing for the diagram keyword — making it more robust for real-world pasted content.
Lazy loading
The mermaid dependency is roughly 2 MB. The paste handler above already lazy-loads with await import('@tldraw/mermaid') — the package is only fetched when the pasted text matches a Mermaid keyword. For your own integration, the same pattern applies: pre-screen with a lightweight regex, then dynamic-import the package only when needed.
Examples
See the Mermaid diagrams example in the examples app for a runnable demo that renders many diagram types at once. Run it locally with yarn dev from the repo root and visit localhost:5420.
Documentation
Documentation for the most recent release can be found on tldraw.dev/docs, including reference docs. Our release notes can be found here.
For more agent-friendly docs, see our LLMs.txt.
License
This project is part of the tldraw SDK. It is provided under the tldraw SDK license.
Trademarks
Copyright (c) 2024-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our trademark guidelines for info on acceptable usage.
Distributions
You can find tldraw on npm here.
Contribution
Found a bug? Please submit an issue.
Community
Have questions, comments or feedback? Join our discord. For the latest news and release notes, visit tldraw.dev.
Contact
Find us on Twitter/X at @tldraw.