
Security News
Socket Releases Free Certified Patches for Critical vm2 Sandbox Escape
A critical vm2 sandbox escape can allow untrusted JavaScript to break isolation and execute commands on the host Node.js process.
@tldraw/mermaid
Advanced tools
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.
npm i @tldraw/mermaid
Peer dependencies: your app must also include compatible versions of:
tldraw β the SDK this package plugs intoreact β ^18.2.0 or ^19.2.1react-dom β ^18.2.0 or ^19.2.1import { 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.
| 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 |
| 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 })
},
})
createMermaidDiagram(editor, text, options?)Parses Mermaid text, extracts layout from the rendered SVG, builds a blueprint, and creates tldraw shapes.
editor β a tldraw Editor instance.text β Mermaid diagram source text.options β optional MermaidDiagramOptions:
mermaidConfig β Mermaid configuration overrides (theme, spacing, etc.).blueprintRender β positioning (position, centerOnPosition) and optional mapNodeToRenderSpec (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).
MermaidDiagramErrorError class with type: 'parse' | 'unsupported' and diagramType properties.
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).
DiagramMermaidBlueprint β { diagramKind, nodes, edges, lines?, groups? }.MermaidBlueprintNode β layout, semantic kind, and style fields (materialization is chosen at render time).MermaidBlueprintNodeRenderSpec β variant: 'geo' + geo, or variant: '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 optional mapNodeToRenderSpec for renderBlueprint / createMermaidDiagram.MermaidNodeCreateFunctionArgs β argument object for defaultCreateMermaidNodeFromBlueprint (includes resolved render spec).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"]
getBBox().DiagramMermaidBlueprint.renderBlueprint resolves each nodeβs materialization spec (mapper or default), then creates tldraw shapes (geo or custom), plus arrows, lines, frames, and groups.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.
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.
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.
This project is part of the tldraw SDK. It is provided under the tldraw SDK license.
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.
You can find tldraw on npm here.
Please see our contributing guide. Found a bug? Please submit an issue.
Have questions, comments or feedback? Join our discord. For the latest news and release notes, visit tldraw.dev.
Find us on Twitter/X at @tldraw.
FAQs
Mermaid diagram to tldraw shape conversion.
We found that @tldraw/mermaid demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
A critical vm2 sandbox escape can allow untrusted JavaScript to break isolation and execute commands on the host Node.js process.

Research
Five malicious NuGet packages impersonate Chinese .NET libraries to deploy a stealer targeting browser credentials, crypto wallets, SSH keys, and local files.

Security News
pnpm 11 turns on a 1-day Minimum Release Age and blocks exotic subdeps by default, adding safeguards against fast-moving supply chain attacks.