Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
215 lines
10 KiB
Markdown
215 lines
10 KiB
Markdown
# @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 `<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`](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).
|