Mermaid diagram not rendering

Mermaid fails in three visible ways: a bomb icon that says Syntax error in text, the raw code sitting on the page as text, or nothing at all. Each one has a different cause, and the browser console — not the diagram — tells you which.

Match the symptom

What you see Cause Section
Bomb icon, Syntax error in text, mermaid version 11.x The parser rejected a line Syntax error in text
No diagram type detected matching given configuration for text First line isn't a known type, or Mermaid is too old No diagram type detected
The code shows as plain text in your exported HTML Mermaid never found the block The diagram shows as code
Uncaught ReferenceError: mermaid is not defined Script order, or the CDN was blocked Nothing renders, no error
Cannot use import statement outside a module The ESM build loaded without type="module" Nothing renders, no error
Diagram renders tiny, squashed or with clipped labels Rendered while hidden Tiny or squashed
Worked in v9, renders nothing after upgrading render() became async in v10 Broke after upgrading
GitHub shows Unable to render rich display Syntax error, or a type GitHub's Mermaid doesn't know Syntax error · No diagram type

The fastest way to find the bad line: paste the code into the free Mermaid viewer. It renders as you type, shows the line Mermaid rejected, and keeps the last good version on screen while you fix it.

Syntax error in text

The bomb means the parser stopped. Mermaid's own console message names the line and what it expected (Expecting 'SQE', 'DOUBLECIRCLEEND', … got 'PS'). The token names are cryptic; the culprits are not. In flowcharts, almost every case is one of these:

Brackets, parentheses or quotes inside a label. (, [, { and > open node shapes, so a label that contains them ends early.

%% breaks
A[Call fetch(url)] --> B{Retry? [max 3]}

%% works — wrap the label in double quotes
A["Call fetch(url)"] --> B{"Retry? [max 3]"}

A literal double quote inside a quoted label needs an entity code: A["Say #quot;hi#quot;"]. Mermaid also accepts numeric codes such as #35; for #.

A node called end. In a flowchart, lowercase end closes a subgraph. A node named end breaks the parse. Capitalise it (End, END) or give the node a different id with end as its label: done[end].

A node id starting with o or x after a link. A---oB is not "A to oB" — ---o is a circle-ended edge, and ---x a cross-ended one. Add a space (A--- oB) or capitalise the id.

Smaller ones worth checking: a stray semicolon or arrow at the end of a line that started a new statement; -->|label| with a space between --> and | in an older Mermaid; tabs pasted from a doc where the grammar expects indentation in a mindmap; and smart quotes (“ ”) from a word processor, which are not ".

No diagram type detected

The first non-comment line must be a diagram keyword: flowchart TD (or graph TD), sequenceDiagram, classDiagram, stateDiagram-v2, erDiagram, gantt, pie, journey, gitGraph, mindmap, timeline, quadrantChart, xychart-beta, sankey-beta, block-beta and so on. Three things produce this error:

  1. A typo — flowchat, sequence, stateDiagram-V2. Keywords are case sensitive.
  2. Something above it — a heading, a blank code fence marker, or prose pasted along with the diagram. %% comments and a %%{init: …}%% directive are fine; anything else is not.
  3. A version gap — newer types (architecture-beta, packet-beta, kanban) need Mermaid 11. The page you're rendering on may pin an older release. This is also the usual reason a diagram renders in the Mermaid Live Editor and fails on GitHub, Confluence or a docs site: each one bundles its own Mermaid and upgrades on its own schedule.

The diagram shows as code

You exported Markdown to HTML — with pandoc, marked, Python-Markdown, an Obsidian or VS Code export — and the diagram arrived as a code block. That is expected. A ```mermaid fence becomes:

<pre><code class="language-mermaid">flowchart TD …</code></pre>

and Mermaid, by default, only renders elements with class mermaid. GitHub, GitLab and Notion swap the block for you; a static export doesn't, and it usually doesn't include mermaid.js at all. Add both:

<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
<script>
  document.querySelectorAll("code.language-mermaid").forEach((code) => {
    const pre = document.createElement("pre");
    pre.className = "mermaid";
    pre.textContent = code.textContent;
    code.parentElement.replaceWith(pre);
  });
  mermaid.initialize({ startOnLoad: false });
  mermaid.run();
</script>

textContent matters: it hands Mermaid --> rather than the --&gt; that sits in the HTML source. Or paste just the diagram into the Mermaid viewer and publish the rendered result.

Nothing renders, no error

Open the console. If it is empty, Mermaid never ran:

  • startOnLoad is off and nothing calls run(). With the classic build, mermaid.initialize({ startOnLoad: true }) renders on page load; with startOnLoad: false you must call mermaid.run() yourself.

  • mermaid is not defined — your initialize call runs before the <script src> that defines it, the CDN URL 404s (check the version in the path), or an ad blocker or corporate proxy dropped the request.

  • Cannot use import statement outside a module — you copied the ESM snippet without its type:

    <script type="module">
      import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
      mermaid.initialize({ startOnLoad: true });
    </script>
    

    mermaid.esm.min.mjs only works in a module script; mermaid.min.js only works as a classic one. Mixing them is the single most common copy-paste bug.

  • The content was added after load. startOnLoad renders what is in the DOM on load. Diagrams inserted later — by a framework, a fetch, or an agent streaming HTML — need await mermaid.run({ nodes: [el] }).

Tiny or squashed

Mermaid measures each label's text to size the boxes. In a display: none tab, a closed <details>, a collapsed accordion or an unopened slide, every measurement comes back zero, and the diagram renders as a crumpled sliver — or with every label clipped.

Render when the container is shown:

details.addEventListener("toggle", async () => {
  if (!details.open) return;
  const el = details.querySelector(".mermaid");
  el.removeAttribute("data-processed"); // Mermaid skips nodes it already did
  await mermaid.run({ nodes: [el] });
});

The same thing happens in print: a diagram rendered at screen width and then scaled into an A4 page looks small. If the diagram is for a PDF, see print backgrounds missing for the print-CSS side.

Broke after upgrading

Mermaid 10 changed the API that most hand-written integrations used:

Mermaid 9 Mermaid 10 and 11
mermaid.render(id, code, (svg) => …) const { svg } = await mermaid.render(id, code)
const svg = mermaid.render(id, code) Returns a promise — await it
mermaid.init(undefined, ".mermaid") await mermaid.run({ querySelector: ".mermaid" })
mermaid.parse(code) returns a boolean await mermaid.parse(code) resolves, or throws on error

Code that still passes a callback renders nothing and throws no useful error, because the callback is simply never called.

Click handlers stopped working? click A callback that calls a JavaScript function needs securityLevel: "loose"; the default, "strict", disables it. Only loosen it for diagrams you wrote — Mermaid source from an untrusted user should stay strict.

Sharing the diagram once it renders

The diagram now works in your browser. The person you send it to still sees forty lines of participant in Slack, email or Jira, because none of those run Mermaid — the full list.

Publish the page instead. Comma renders reports in a sandboxed iframe (allow-scripts, no allow-same-origin) and allows cdn.jsdelivr.net, so the mermaid@11 script above loads and the diagram renders for the reader, on a phone too. Then they can select the paragraph next to the arrow they disagree with and comment there, instead of screenshotting it into a thread (comment on HTML).

Or start from the source: paste it into the Mermaid viewer, fix the syntax error, and get a shareable link in the same step.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Open the Mermaid viewer →

Related