How to Create a Custom Diagram Type in Mermaid Using `registerLazyLoadedDiagrams` and `addDetector`

You can extend Mermaid with custom diagram types by implementing a detector function to identify your syntax and a loader function to import your rendering logic, then registering both via registerLazyLoadedDiagrams or addDetector before initializing the library.

The mermaid-js/mermaid repository exposes a lazy-loaded diagram API that allows third-party developers to plug new visualization types into the parser without modifying core source code. This architecture relies on two primary functions exported from packages/mermaid/src/diagram-api/detectType.ts: registerLazyLoadedDiagrams (lines 66‑70) for batch registration and addDetector (lines 72‑78) for individual additions.

Understanding the Core API Components

Mermaid’s extensibility model separates detection from implementation. This keeps initial bundle sizes small while allowing complex custom renderers to load on demand.

The Detector Function

A detector is a function that inspects raw diagram text and returns a truthy value if the content belongs to your custom type. According to the source code in detectType.ts (lines 41‑46), Mermaid iterates over all registered detectors during the parsing phase:

for (const [key, { detector }] of Object.entries(detectors)) {
    const diagram = detector(text, config);
    if (diagram) return key;
}

Your detector receives the raw text and optional configuration, allowing you to match against keywords, frontmatter, or specific syntax patterns.

The Loader Function

The loader is an async function that imports your diagram’s implementation only when needed. This lazy-loading mechanism prevents unused diagram logic from bloating the initial JavaScript bundle. When a detector match occurs, Mermaid retrieves the associated loader via getDiagramLoader(key) and executes it to obtain the DiagramDefinition object containing your parser, database, and renderer.

Step-by-Step Implementation

Follow this complete workflow to build and register a custom diagram named myDiagram.

Step 1: Implement the Diagram Module

Create a file that exports a DiagramDefinition object conforming to Mermaid’s internal interface. This example renders a static SVG rectangle:

// myDiagram.ts
import type {
  DiagramDefinition,
  DiagramRenderer,
  DiagramDB,
} from 'mermaid';

const db: DiagramDB = {};

const renderer: DiagramRenderer = {
  draw: async (text, id, version, diagram) => {
    const container = document.getElementById(id);
    if (!container) return;
    
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('width', '200');
    svg.setAttribute('height', '100');
    
    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
    rect.setAttribute('x', '10');
    rect.setAttribute('y', '10');
    rect.setAttribute('width', '180');
    rect.setAttribute('height', '80');
    rect.setAttribute('fill', '#f9f');
    
    const textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    textEl.setAttribute('x', '100');
    textEl.setAttribute('y', '55');
    textEl.setAttribute('text-anchor', 'middle');
    textEl.setAttribute('fill', '#000');
    textEl.textContent = 'My Custom Diagram';
    
    svg.appendChild(rect);
    svg.appendChild(textEl);
    container.appendChild(svg);
  },
};

export const diagram: DiagramDefinition = {
  db,
  renderer,
  parser: { parse: () => {} },
};

Step 2: Define the Detector and Loader

Create a plugin file that defines the ExternalDiagramDefinition object. The detector checks if the diagram text starts with your custom keyword, while the loader dynamically imports the implementation:

// myDiagramPlugin.ts
import type {
  ExternalDiagramDefinition,
  DiagramDetector,
  DiagramLoader,
} from 'mermaid';

const detector: DiagramDetector = (text) => {
  const firstWord = text.trim().split(/\s+/)[0];
  return firstWord === 'myDiagram';
};

const loader: DiagramLoader = async () => {
  const mod = await import('./myDiagram');
  return { id: 'myDiagram', diagram: mod.diagram };
};

export const myDiagramPlugin: ExternalDiagramDefinition = {
  id: 'myDiagram',
  detector,
  loader,
};

Step 3: Register Before Initialization

Import and register your diagram before calling mermaid.initialize(). As implemented in detectType.ts (lines 66‑70), registerLazyLoadedDiagrams forwards each definition to addDetector (lines 72‑78), which stores the detector and loader in an internal map:

import mermaid from 'mermaid';
import { myDiagramPlugin } from './myDiagramPlugin';

mermaid.registerLazyLoadedDiagrams(myDiagramPlugin);

mermaid.initialize({
  startOnLoad: true,
});

If you need to register diagrams individually rather than in batch, use mermaid.addDetector(key, detector, loader) directly.

Step 4: Use in Markdown

Reference your custom diagram using the identifier defined in the plugin:


```myDiagram
optional configuration or data here

When Mermaid processes this block, the `detectType` function iterates through registered detectors, identifies your custom type, lazy-loads the implementation via your loader function, and renders the SVG.

## How Detection Works Under the Hood

The type detection logic resides in [`packages/mermaid/src/diagram-api/detectType.ts`](https://github.com/mermaid-js/mermaid/blob/main/packages/mermaid/src/diagram-api/detectType.ts). When parsing begins:

1. **Registration**: `registerLazyLoadedDiagrams` accepts an array of `ExternalDiagramDefinition` objects and invokes `addDetector` for each, storing entries in a private `detectors` map (lines 72‑78). If a duplicate key exists, a warning is emitted.

2. **Parsing**: The `detectType` function (lines 36‑46) strips frontmatter and comments from input text, then iterates through the `detectors` map, invoking each detector function until one returns truthy.

3. **Lazy Loading**: Upon successful detection, Mermaid retrieves the associated loader via `getDiagramLoader(key)` and executes it, importing your diagram module only at render time rather than at application startup.

This architecture ensures that custom diagrams integrate seamlessly with Mermaid’s built-in types while maintaining optimal performance through on-demand code splitting.

## Summary

- **Use `registerLazyLoadedDiagrams`** to batch-register one or more `ExternalDiagramDefinition` objects containing a unique `id`, `detector` function, and optional `loader` function.
- **Call registration before initialization** to ensure your detectors are available when `mermaid.initialize()` runs.
- **Implement `DiagramDetector`** to identify your custom syntax by inspecting the raw diagram text.
- **Provide a `DiagramLoader`** to asynchronously import your rendering logic, keeping bundle sizes minimal until the diagram is actually used.
- **Reference [`packages/mermaid/src/diagram-api/detectType.ts`](https://github.com/mermaid-js/mermaid/blob/main/packages/mermaid/src/diagram-api/detectType.ts)** (lines 66‑78) for the core registration logic and [`packages/mermaid/src/diagram-api/types.ts`](https://github.com/mermaid-js/mermaid/blob/main/packages/mermaid/src/diagram-api/types.ts) for TypeScript interfaces.

## Frequently Asked Questions

### What is the difference between `registerLazyLoadedDiagrams` and `addDetector`?

`registerLazyLoadedDiagrams` is a convenience batch method that accepts multiple `ExternalDiagramDefinition` objects and internally calls `addDetector` for each one (see [`detectType.ts`](https://github.com/mermaid-js/mermaid/blob/main/detectType.ts) lines 66‑70). `addDetector` handles individual registration directly, storing the detector and loader in the internal map (lines 72‑78). Use the former for registering multiple diagrams at once, and the latter for single, dynamic registrations.

### Can I override built-in Mermaid diagrams with custom detectors?

Yes, but Mermaid emits a warning when you register a detector with an `id` that already exists in the internal map. The new detector will overwrite the previous entry, allowing you to customize behavior for standard diagram types like `flowchart` or `sequence`. However, overriding built-ins is not recommended unless you specifically need to modify core parsing behavior.

### What parameters does the detector function receive?

The `DiagramDetector` function receives two parameters: `text` (the raw diagram definition string) and `config` (the global Mermaid configuration object). You can use the configuration to enable conditional detection based on user settings, though most implementations only inspect the text content to identify the diagram type.

### Is lazy loading mandatory for custom diagrams?

No, lazy loading is optional. If you omit the `loader` property in your `ExternalDiagramDefinition`, Mermaid expects the diagram implementation to be bundled with your application. However, providing a loader function is strongly recommended for large or complex diagrams to prevent unnecessary JavaScript from loading on pages that do not use your custom type.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →