Mermaid diagrams
You can turn Mermaid diagram syntax into native, editable tldraw shapes with the @tldraw/mermaid package. Instead of rendering a static SVG, it parses Mermaid text and creates real geo shapes, arrows, and groups on the canvas. Users can move, resize, restyle, and connect them like any other shape.
Quick start
Install the package alongside tldraw:
npm install @tldraw/mermaidThen call createMermaidDiagram with an Editor and a Mermaid source string:
import { createMermaidDiagram } from '@tldraw/mermaid'
await createMermaidDiagram(
editor,
`
flowchart TD
A[Start] --> B{Decision}
B -->|Yes| C[Do something]
B -->|No| D[Do something else]
`
)By default the diagram is centered on the viewport, or on the pointer when the user's paste-at-cursor preference is on. Pass a blueprintRender.position to place it at a specific page point, and set centerOnPosition: false to anchor its top-left corner instead of its center. The finished diagram is a single group. Pass mermaidConfig to override Mermaid's layout settings, such as node spacing or font size.
Supported diagram types
| Diagram type | Mermaid keyword | What you get |
|---|---|---|
| Flowchart | flowchart, graph | Geo shapes, arrows, subgraph containers |
| Sequence diagram | sequenceDiagram | Actor shapes, lifelines, signal arrows, fragment containers |
| State diagram | stateDiagram, stateDiagram-v2 | State shapes, transitions, compound state containers, fork/join, choice |
| Mindmap | mindmap | Colored geo shapes, parent-child edges, tree hierarchy |
Subgraphs, fragments, and compound states become geo rectangles with their child shapes parented to them.
For unsupported types such as pie, gantt, class, and ER, pass an onUnsupportedDiagram callback. Here it falls back to importing Mermaid's rendered SVG:
await createMermaidDiagram(editor, text, {
async onUnsupportedDiagram(svgString) {
await editor.putExternalContent({ type: 'svg-text', text: svgString })
},
})Without a callback, createMermaidDiagram throws a MermaidDiagramError for unsupported types and parse failures.
Handling pasted Mermaid text
The most common integration is converting Mermaid text when users paste it onto the canvas. Register an external content handler that sniffs for a Mermaid keyword and dynamically imports the package:
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 pasting Mermaid text will render it as shapes. The Mermaid pasting example extends this with markdown fence stripping and a toast for unsupported diagrams.
The mermaid dependency is ~2 MB. createMermaidDiagram loads it lazily on first call, and the
regex above keeps it from being called for ordinary text, so users who never paste Mermaid don't
pay the cost.
Customizing node shapes
By default, blueprint nodes are materialized as tldraw geo shapes. To render them as your own custom shape type instead, pass mapNodeToRenderSpec on blueprintRender:
await createMermaidDiagram(editor, text, {
blueprintRender: {
mapNodeToRenderSpec(input) {
if (input.diagramKind === 'flowchart') {
return {
variant: 'shape',
type: 'pipeline-step',
props: { mermaidNodeId: input.nodeId },
}
}
// Return undefined to keep the package default for this node.
return undefined
},
},
})The callback receives diagramKind, nodeId, kind, and the full MermaidBlueprintNode, and returns a MermaidBlueprintNodeRenderSpec: either a geo variant or a custom shape type that's registered on the editor. The package sets w, h, fill, color, dash, size, richText, align, and verticalAlign on every node it creates, so a custom shape's props must accept those keys or validation will fail. See createMermaidDiagram and renderBlueprint in the reference for the full API.
Examples
- Hundreds of Mermaid diagrams — A runnable demo rendering many diagram types at once
- Customize Mermaid diagrams — Converting Mermaid diagram nodes into custom shapes
- Pasting Mermaid code as shapes — The paste handler above, end to end
Related
createMermaidDiagram,renderBlueprint,MermaidDiagramError— Entry points and errorsDiagramMermaidBlueprint,MermaidBlueprintNode,MermaidBlueprintNodeRenderSpec— The data model you can customize- Shapes — Define a custom shape you can map Mermaid nodes to
- External content handlers — More on
registerExternalContentHandlerfor the paste integration