shikidown
Guides

Mermaid diagrams

Render fenced mermaid blocks as SVG, through an entry point that keeps mermaid out of every other bundle.

shikidown recognises ```mermaid fences natively — no markdown-it plugin required. Instead of highlighting the fence, MarkdownService emits a placeholder:

<pre class="mermaid" data-mermaid-src="…">graph TD…</pre>

Turning that placeholder into an SVG is the job of MermaidDirective, which lives in its own entry point.

Setup

Install mermaid

npm install mermaid

mermaid is an optional peer dependency (>=11).

Add the directive

import { MarkdownComponent } from 'shikidown';
import { MermaidDirective } from 'shikidown/mermaid';

@Component({
  selector: 'app-docs',
  imports: [MarkdownComponent, MermaidDirective],
  template: `<shikidown mermaid [content]="markdown" />`,
})
export class DocsComponent {
  readonly markdown = '```mermaid\ngraph TD\n  A[Start] --> B{Choice}\n```';
}

MermaidDirective has an attribute selector, [mermaid], and takes no inputs.

Without the directive, ```mermaid blocks stay rendered as inert code. That is a deliberate, harmless fallback rather than an error.

Why a separate entry point

mermaid is large, and most applications never draw a diagram. It is therefore reachable only through shikidown/mermaid:

import { MermaidDirective } from 'shikidown/mermaid'; // ← the only door

If you never import from that path, mermaid never enters your dependency graph. And even when you do, the package itself is fetched with a dynamic import('mermaid') the first time the directive runs — never during SSR, never before it is needed.

How rendering is driven

The directive is event-driven rather than re-running on every change detection cycle. It installs two MutationObservers:

  • one on its host element, watching for diagram placeholders newly inserted by shikidown — which is what makes it work with asynchronous and incremental rendering;
  • one on <html>, watching the class attribute for a dark/light switch.

Work is coalesced through a microtask, so the burst of mutations mermaid itself produces while drawing does not trigger a second pass. Nothing runs while nothing changes, and rendering is scoped to the host element rather than the whole document.

Dark mode

On a theme change the directive re-initialises mermaid with theme: 'dark' or theme: 'default', then rearms already-rendered diagrams by restoring their source from data-mermaid-src and clearing data-processed. Diagrams are redrawn in the new theme without touching [content].

The trigger is the dark class on <html> — the same convention used by Styling and dark mode.

Security

The directive initialises mermaid with securityLevel: 'strict': HTML in labels is encoded and click handlers are disabled. For interactive diagrams you trust, opt into 'loose':

<shikidown mermaid mermaidSecurityLevel="loose" [content]="markdown" />

'loose' allows click handlers and raw HTML inside diagram labels. Never use it for diagrams produced by an LLM or a user. See Content security.

Invalid diagrams and streaming

Each diagram is parsed before rendering. An invalid one keeps its source on screen, marked with data-mermaid-error, without preventing the others from rendering. In streaming mode, the pre.mermaid placeholder is only emitted once the fence is closed: an unfinished diagram is shown as code, never half-rendered.

Example

```mermaid
flowchart LR
  Editor -->|content| shikidown
  shikidown -->|pre.mermaid| MermaidDirective
  MermaidDirective -->|SVG| DOM
```

On this page