Files
upstream-mirror a774ac673a
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
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

10 KiB
Raw Permalink Blame History

@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 into
  • react — ^18.2.0 or ^19.2.1
  • react-dom — ^18.2.0 or ^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 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:

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:

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.