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:
- A typo —
flowchat,sequence,stateDiagram-V2. Keywords are case sensitive. - 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. - 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 --> 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:
startOnLoadis off and nothing callsrun(). With the classic build,mermaid.initialize({ startOnLoad: true })renders on page load; withstartOnLoad: falseyou must callmermaid.run()yourself.mermaid is not defined— yourinitializecall 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 itstype:<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.mjsonly works in a module script;mermaid.min.jsonly works as a classic one. Mixing them is the single most common copy-paste bug.The content was added after load.
startOnLoadrenders what is in the DOM onload. Diagrams inserted later — by a framework, a fetch, or an agent streaming HTML — needawait 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.