# @tldraw/mermaid Convert [Mermaid](https://mermaid.js.org/) 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 ```bash npm i @tldraw/mermaid ``` **Peer dependencies:** your app must also include compatible versions of: - **`tldraw`** — the SDK this package plugs into - **`react`** — `^18.2.0` or `^19.2.1` - **`react-dom`** — `^18.2.0` or `^19.2.1` ## Quick start ```tsx 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`: ```tsx // 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: ```tsx 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 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). ### `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, 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). ## How it works The conversion happens in three stages: ```mermaid flowchart LR MermaidText["Mermaid text"] --> ParseRender["Parse and render SVG"] ParseRender --> ExtractBlueprint["Extract blueprint"] ExtractBlueprint --> CreateShapes["Create tldraw shapes"] ``` 1. **Parse and render** — Mermaid parses the text and renders an SVG into an offscreen DOM element, giving us accurate layout positions via `getBBox()`. 2. **Extract blueprint** — a diagram-specific converter reads positions from the SVG and semantic data from Mermaid's internal database, producing a `DiagramMermaidBlueprint`. 3. **Create shapes** — `renderBlueprint` resolves 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](https://github.com/tldraw/tldraw/blob/main/apps/dotcom/client/src/components/SneakyMermaidHandler/SneakyMermaidHandler.tsx): ```tsx 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 `` inside your `` 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`](https://github.com/tldraw/tldraw/blob/main/apps/dotcom/client/src/components/SneakyMermaidHandler/simpleMermaidStringTest.ts), 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](https://github.com/tldraw/tldraw/tree/main/apps/examples/src/examples/use-cases/hundred-mermaids) 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](https://tldraw.dev/docs), including [reference docs](https://tldraw.dev/reference/editor/Editor). Our release notes can be found [here](https://tldraw.dev/releases). For more agent-friendly docs, see our [LLMs.txt](https://tldraw.dev/llms.txt). ## License This project is part of the tldraw SDK. It is provided under the [tldraw SDK license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md). ## Trademarks Copyright (c) 2024-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our [trademark guidelines](https://github.com/tldraw/tldraw/blob/main/TRADEMARKS.md) for info on acceptable usage. ## Distributions You can find tldraw on npm [here](https://www.npmjs.com/package/@tldraw/tldraw?activeTab=versions). ## Contribution Found a bug? Please [submit an issue](https://github.com/tldraw/tldraw/issues/new). ## Community Have questions, comments or feedback? [Join our discord](https://discord.tldraw.com/?utm_source=github&utm_medium=readme&utm_campaign=sociallink). For the latest news and release notes, visit [tldraw.dev](https://tldraw.dev). ## Contact Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw).