Mermaid Flowchart: Syntax, 53 Shapes and 15 Traps, Tested

On this page
  1. The first line: direction
  2. Nodes: brackets and the 53 named shapes
  3. Arrows and lines
  4. Text inside nodes
  5. Subgraphs, swimlanes and collapsed groups
  6. Colours and styling
  7. What Mermaid 12 changed for flowcharts
  8. If/else, loops and parallel steps
  9. 15 lines that break a flowchart, or quietly lie
  10. From Mermaid to Excalidraw shapes
  11. Mermaid flowcharts in Jira and Confluence
  12. Sources

The architecture page looked fine on Friday. On Monday it says Lexical error on line 1. Unrecognized text. Someone pasted a flowchart from a guide that writes flowchart td, and Mermaid only accepts the direction in capitals. The line looks right, which is why it takes ten minutes to find.

This page answers the questions people type about Mermaid flowcharts, one by one, and every answer was drawn, not copied from the docs. We ran 98 small flowcharts through Mermaid 11.17.2 and 12.3.0 (the latest, 3 November 2026) in a headless browser on 11 November 2026, and converted them to Excalidraw shapes with two versions of the converter Excalidraw uses.

Short answer: a flowchart is flowchart LR (or TD) followed by one link per line:

flowchart LR
  A([Refund request]) --> B{Paid by card?}
  B -->|yes| C[Refund to card]
  B -->|no| D[(Wallet)]

The brackets pick the shape, the arrow picks the line, |text| labels it. Since Mermaid 12 (September 2026) the same code is drawn with ELK layout, square edges and wider, white boxes. Write the direction in capitals, never name a node end, and quote labels that contain brackets.

The first line: direction

You writeDirection
flowchart TDTop to bottom
flowchart TBTop to bottom, identical to TD
flowchart BTBottom to top
flowchart LRLeft to right (“horizontal”)
flowchart RLRight to left
flowchartNo direction: drawn top to bottom
flowchart tdLexical error, in 11 and in 12

TD vs TB: no difference. Our TD and TB renders had the same node positions in both versions. flowchart vs graph: graph TD is the older keyword and drew the same as flowchart TD in our test; new features are documented under flowchart, so use that. A title goes in front matter above the first line (---, title: Refund flow, ---), and %% starts a comment line.

Nodes: brackets and the 53 named shapes

The classic shapes come from the brackets around the text:

CodeShapeUse it for
A[Text]RectangleA step
A(Text)Rounded rectangleA softer step, an event
A([Text])StadiumStart or end
A[[Text]]SubroutineA call to another flow
A[(Text)]CylinderA database
A((Text))CircleA connector or state
A>Text]Flag (asymmetric)A signal or note
A{Text}DiamondA decision, yes or no
A{{Text}}HexagonPreparation
A[/Text/]ParallelogramInput or output
A[\Text\]Parallelogram, other slantOutput
A[/Text\]TrapezoidManual operation
A[\Text/]Trapezoid, upside downManual operation
A(((Text)))Double circleStop

Since Mermaid 11.3 there is a second syntax that names the shape: A@{ shape: doc, label: "Invoice" }. We parsed the shape list out of Mermaid’s source at both tags: 53 shapes, the same 53 in 11.17.2 and 12.3.0, each with a short name and aliases (cyl, db, database and cylinder are one shape). Here is every one, drawn by Mermaid 12.3.0 with its short name as the label:

All 53 Mermaid flowchart shapes drawn by Mermaid 12.3.0 in the classic look, nine rows of six, each labelled with the short name you write after shape: rect, rounded, stadium, fr-rect, cyl, datastore; folder, bucket, console, browser, person, circle; bang, cloud, diam, hex, lean-r, lean-l; trap-b, trap-t, dbl-circ, text, notch-rect, lin-rect; then sm-circ, fr-circ, fork, hourglass, brace, brace-r, where the small circle, framed circle, fork bar and hourglass show no label; braces, bolt (no label), doc, delay, h-cyl, lin-cyl; curv-trap, div-rect, tri, win-pane, f-circ (a filled dot, no label), notch-pent; flip-tri, sl-rect, docs, st-rect, bow-rect, cross-circ (no label); tag-doc, tag-rect, flag, odd, lin-doc.

Every short name with the meaning Mermaid’s source gives it and the other names it accepts:

Short nameMeaningAlso accepted
rectProcessproc, process, rectangle
roundedEventevent
stadiumTerminal Pointterminal, pill
fr-rectSubprocesssubprocess, subproc, framed-rectangle, subroutine
cylDatabasedb, database, cylinder
datastoreData Storedata-store
folderFolderdirectory
bucketBucketnone
consoleConsolenone
browserBrowsernone
personPersonnone
circleStartcirc
bangBangnone
cloudCloudnone
diamDecisiondecision, diamond, question
hexPrepare Conditionalhexagon, prepare
lean-rData Input/Outputlean-right, in-out
lean-lData Input/Outputlean-left, out-in
trap-bPriority Actionpriority, trapezoid-bottom, trapezoid
trap-tManual Operationmanual, trapezoid-top, inv-trapezoid
dbl-circStopdouble-circle
textText Blocknone
notch-rectCardcard, notched-rectangle
lin-rectLined/Shaded Processlined-rectangle, lined-process, lin-proc, shaded-process
sm-circStart (no label)start, small-circle
fr-circStop (no label)stop, framed-circle
forkFork/Join (no label)join
hourglassCollate (no label)collate
braceCommentcomment, brace-l
brace-rComment Rightnone
bracesComment with braces on both sidesnone
boltCom Link (no label)com-link, lightning-bolt
docDocumentdocument
delayDelayhalf-rounded-rectangle
h-cylDirect Access Storagedas, horizontal-cylinder
lin-cylDisk Storagedisk, lined-cylinder
curv-trapDisplaycurved-trapezoid, display
div-rectDivided Processdiv-proc, divided-rectangle, divided-process
triExtractextract, triangle
win-paneInternal Storageinternal-storage, window-pane
f-circJunction (no label)junction, filled-circle
notch-pentLoop Limitloop-limit, notched-pentagon
flip-triManual Filemanual-file, flipped-triangle
sl-rectManual Inputmanual-input, sloped-rectangle
docsMulti-Documentdocuments, st-doc, stacked-document
st-rectMulti-Processprocs, processes, stacked-rectangle
bow-rectStored Datastored-data, bow-tie-rectangle
cross-circSummary (no label)summary, crossed-circle
tag-docTagged Documenttagged-document
tag-rectTagged Processtagged-rectangle, tag-proc, tagged-process
flagPaper Tapepaper-tape
oddOddnone
lin-docLined Documentlined-document

Three things the docs do not show you:

  • Seven shapes draw no label at all, even when you give one: sm-circ and fr-circ (start and stop dots), fork, hourglass, bolt, f-circ (the junction dot) and cross-circ. Put the text on the link instead.
  • A wrong name stops the diagram. @{ shape: queue } fails with No such shape: queue. in both versions; there is no queue shape. h-cyl (a horizontal cylinder) is the nearest.
  • Shapes for people and systems exist: person, browser, console, folder, bucket and cloud are in the list. A decision is diam, a document doc, several documents docs, a junction where lines merge f-circ, and a fork or join bar fork.

Icons: U@{ icon: "fa:user", label: "User" } needs an icon pack registered by the page that renders the diagram (Mermaid’s icon docs). Without one we got a blue square with a question mark. The old fa:fa-user inside a label needs Font Awesome’s CSS on the page; without it only the word “User” appeared.

Arrows and lines

Twelve Mermaid flowchart links drawn by Mermaid 12.3.0, each box labelled with its code: A --> B solid arrow, A --- B solid line with no head, A -.-> B dotted arrow, A -.- B dotted line, A ==> B thick arrow, A === B thick line, A --o B line ending in a filled circle, A --x B line ending in a cross, A <--> B arrow heads at both ends, A o--o B circles at both ends, A x--x B crosses at both ends, and A ~~~ B with no line at all, the invisible link.
  • Labels: A -->|yes| B and A -- yes --> B are the same. Dotted and thick links take text the same way: A -. maybe .-> B, A == always ==> B.
  • Longer links: each extra dash should make the link span one more rank. In Mermaid 11, A ----> C put C one level below B. In Mermaid 12 the extra dashes did nothing: ELK placed B and C side by side. With layout: dagre in front matter, 12 drew C one level lower again.
  • Chains and fan-out: A --> B --> C is two links on one line. A & B --> C & D is four.
  • Reverse arrows do not exist. B <-- A is a parse error (Expecting 'LINK'). Write A --> B, or <--> for both ends.
  • Invisible links: A ~~~ B adds no line but keeps the two nodes next to each other. It is the closest thing to nudging positions, because Mermaid has no x/y for nodes.

Edge ids give a link its own settings. Put an id before the arrow, then attach settings with @{ }:

flowchart LR
  A e1@--> B
  A e2@--> C
  e1@{ animate: true }
  e2@{ curve: linear }

animate: true (or animation: fast / slow) makes the dashes move. curve sets one link’s shape; for all links set it in front matter (config:, flowchart:, curve: linear). Mermaid 12.3.0 accepts basis (the default), bumpX, bumpY, cardinal, catmullRom, linear, monotoneX, monotoneY, natural, step, stepAfter, stepBefore and rounded. For straight lines use linear, for right angles step.

Text inside nodes

  • Brackets, slashes and quotes: quote the whole label. A[Call f(x)] is a parse error; A["Call f(x)"] works. Inside quotes, write a double quote as #quot;.
  • Symbols: entity codes work in labels: #9829; is ♥, #35; is #. Unicode and emoji work as they are: A[Заявка] --> B[申请 âś…] drew fine.
  • Line breaks: A[First line<br>second line], or a Markdown string in backticks inside quotes, which also gives bold and italic: A["`**Bold** and _italic_`"] with a real new line inside for a second line.
  • Wrapping and width: Mermaid wraps long labels itself. In 12 the text area is 120 px wide by default (wrappingWidth) and every node is at least that wide (minNodeWidth, new in 12), so a node that says “A” is 152 px wide. Set minNodeWidth: 0 in front matter for snug boxes: our “A” went from 152 to 41 px.
  • The word end: a node called end in lowercase breaks the flowchart (start --> end is a parse error), because end closes a subgraph. End, END or a label such as e[end] all work.

Subgraphs, swimlanes and collapsed groups

flowchart TB
  subgraph Frontend
    UI --> API
  end
  subgraph backend [Back end]
    API --> DB
  end
  Frontend --> backend
  • A subgraph name with a space needs an id: subgraph Back end is a parse error, subgraph backend [Back end] works.
  • Subgraphs nest, and you can link to a whole subgraph by its id (Frontend --> backend).
  • direction LR as the first line inside a subgraph turns it sideways, unless one of its nodes is linked from outside. Then the direction is ignored; the docs say so, and both versions did it in our test. Setting flowchart.inheritDir: true did not change that. What worked: link to the subgraph itself (X --> S), not to a node inside it.
  • Collapse a group (since 11.17.0): one@{ view: collapsed } draws the subgraph one as one node with its title, and links that crossed its edge point at that node. It worked in both versions.

Swimlanes are their own diagram type, swimlane-beta, added in Mermaid 11.16.0 and still in beta (docs). Each top-level subgraph becomes a lane, and the docs warn the syntax may still change:

swimlane-beta LR
  subgraph Customer
    a[Ask for refund]
    f[Get money back]
  end
  subgraph Support
    b[Check order] --> c{Within 30 days?}
  end
  subgraph Finance
    d[Pay back]
  end
  a --> b
  c -->|yes| d
  c -->|no| a
  d --> f
A Mermaid 12.3.0 swimlane-beta diagram with three bands stacked from top to bottom, Customer, Support and Finance, their names written sideways at the left edge. Ask for refund in the Customer band leads down to Check order in Support, then right to the decision Within 30 days?; yes goes down to Pay back in the Finance band, no goes back up to Ask for refund, and Pay back leads up to Get money back in the Customer band.

Before 11.16 the usual workaround was a plain flowchart with one subgraph per team. It draws separate boxes of different sizes side by side, not bands that run across the whole diagram. A legend works the same way: a small subgraph called Legend with one sample node per class, linked with ~~~ so no lines show.

Colours and styling

flowchart LR
  A:::bad --> B --> C
  classDef bad fill:#fee2e2,stroke:#b91c1c
  classDef ok fill:#dcfce7
  class B,C ok
  style C stroke-width:3px
  linkStyle 0 stroke:#d00,stroke-width:3px
  • style styles one node, classDef defines a class, ::: or class attaches it. classDef default restyles every node at once. font-size:24px works in style too.
  • linkStyle counts links from 0 in the order they appear, and linkStyle default sets them all. A number past the last link kills the whole diagram: one link and linkStyle 3 gave Cannot set properties of undefined (setting 'style') in both versions.
  • Themes: in front matter (config:, theme: forest), or the older first line %%{init: {"theme": "dark"}}%%; both worked in 11 and 12. The 12.3.0 schema lists default, base, dark, forest, neutral, neo, neo-dark, redux, redux-dark, redux-color (the new default for flowcharts) and redux-dark-color.
  • Hand-drawn look: look: handDrawn in front matter. In 12 it keeps the white fill of the new theme; add theme: default for the familiar sketch with hatching.
  • Clicks: click A "https://example.com" "Tooltip" _blank makes a node a link. Callbacks such as click A call fn() only run where the page sets securityLevel: 'loose'; the default is strict.

What Mermaid 12 changed for flowcharts

Mermaid 12.0.0 came out on 10 September 2026. The syntax did not change: every source on this page parsed the same in 11.17.2 and 12.3.0. The drawing did. From the 12.3.0 flowchart docs: flowcharts “use the redux-color theme and the neo look by default, and are laid out by ELK rather than Dagre.” The same source in both:

A refund flowchart drawn by Mermaid 11.17.2 with the old defaults: lilac boxes with purple borders, curved thin grey links, a stadium Refund request, a diamond Paid by card?, yes to Refund to card and no to a Wallet cylinder, both into Send receipt inside a yellow Finance subgraph.
The same refund flowchart drawn by Mermaid 12.3.0 with the new defaults: white boxes with dark borders and a soft shadow, thicker black links bent at right angles by the ELK layout, wider boxes of equal width, smaller text, and a pale pink Finance subgraph.
Default (flowchart)11.17.212.3.0
LayoutDagreELK, bundled
Theme and lookdefault, classicredux-color, neo
Node fill, linkLilac #ECECFF, 1 px greyWhite, 2 px black
Label text16 px14 px
Text width before a wrap200 px120 px
Smallest nodeFits the text120 px of text area (minNodeWidth)

To get the old drawing back, put this front matter above the flowchart (the docs give the same three keys):

---
config:
  theme: default
  look: classic
  layout: dagre
---
flowchart LR
  A --> B

One silent change to watch: extra dashes in a link (---->) no longer push a node further down under ELK, as described in the arrows section; layout: dagre brings that back. Two older settings behave differently now. flowchart: defaultRenderer: elk was removed in 12 (use layout: elk), and layout: elk in 11 needs the separate @mermaid-js/layout-elk package: without it, our 11.17.2 render quietly used Dagre and gave no warning.

Step-by-step animation (12.3.0): give links a step number and a dot travels along them in order, which shows a request moving through a system. stepNumbers: true adds a numbered badge to each link, so the order reads in a screenshot or a PDF too. With reduced motion turned on in the operating system, the docs say the dot is hidden. Mermaid 11 ignores step without an error.

---
config:
  flowchart:
    stepNumbers: true
---
flowchart LR
  Client e1@--> API
  API e2@--> Cache
  Cache e3@-->|miss| DB
  DB e4@--> API
  API e5@--> Client
  e1@{ step: 1 }
  e2@{ step: 2 }
  e3@{ step: 3 }
  e4@{ step: 4 }
  e5@{ step: 5 }
A Mermaid 12.3.0 flowchart Client, API, Cache, DB with five links, each carrying a small black numbered badge from 1 to 5 that shows the order of the step animation: Client to API 1, API to Cache 2, Cache to DB 3 labelled miss, DB to API 4 and API back to Client 5.

If/else, loops and parallel steps

Flowcharts have no if or for keywords. A decision is a diamond with labelled links, a loop is a link back:

flowchart TD
  A[Take next item] --> B[Process item]
  B --> C{More items?}
  C -->|yes| A
  C -->|no| D([Done])

Parallel work is a fan-out and a fan-in with &; a fork bar shows it explicitly:

flowchart LR
  A[Build] --> F@{ shape: fork } --> B[Unit tests] & C[Lint] & D[Security scan]
  B & C & D --> J@{ shape: fork } --> E[Deploy]

Notes are not part of the flowchart syntax. The closest are the text shape (a label with no box) and brace-r (a curly brace comment) linked to the step with -.-.

15 lines that break a flowchart, or quietly lie

The first eight stop the diagram with an error. The last seven draw something without a word, which is worse. All 15 behaved the same in Mermaid 11.17.2 and 12.3.0.

You writeWhat happensWrite instead
flowchart tdLexical error on line 1. Unrecognized text.flowchart TD
start --> endParse error on line 2start --> End
A[Call f(x)]Parse error on line 2A["Call f(x)"]
B <-- AParse error on line 3, Expecting 'LINK'A --> B
subgraph Back endParse error on line 3subgraph backend [Back end]
@{ shape: queue }No such shape: queue.a name from the 53, e.g. h-cyl
linkStyle 3 with fewer linksCannot set properties of undefined (setting 'style'), nothing is drawncount links from 0
501 linksEdge limit exceeded. 500 edges found, but the limit is 500.split it, or raise maxEdges in the page
A---oBA link to B ending in a circleA--- oB
dev---xrayA node called ray, link ending in a crossdev--- xray
style A fill:#fff6b6 for a missing idExtra yellow boxes appear, one per idstyle ids that exist
More than 50,000 charactersOne red box: Maximum text size in diagram exceededsplit the diagram
direction LR in a linked subgraphDirection ignoredlink to the subgraph, not inside it
layout: elk in Mermaid 11Dagre layout, no warningadd @mermaid-js/layout-elk, or use 12
@{ icon: "fa:user" } with no icon packA blue “?” squareregister the icon pack

Two notes on the table. The edge limit message counts 500 when the source has 501, and the limit cannot be lifted from inside the diagram: %%{init: {"maxEdges": 1000}}%% gave the same error plus You cannot set this config via configuration inside the diagram as it is a secure config. And the missing-id row is in a popular guide on Google’s first page: its example styles A, B and C in a diagram whose nodes are Start, Medium and End, so the rendered diagram has two extra boxes.

Chart titled One Mermaid flowchart, four renderers, from our renders on 11 November 2026. Columns: Mermaid 11.17.2, Mermaid 12.3.0, Excalidraw shapes from mermaid-to-excalidraw 2.2.2, and from 3.6.0. Nodes, arrows, labels and colours: drawn, drawn, shapes, shapes. 53 named shapes: drawn, drawn, rectangles, rectangles. Subgraphs: drawn, drawn, one picture, shapes. swimlane-beta: drawn, drawn, one picture, shapes. Default layout: Dagre, ELK, Mermaid's, Pele's own. Default look: classic, neo, hand-drawn, hand-drawn. Step animation: ignored, dot and badges, plain arrow, plain arrow. Invisible link: hidden, hidden, visible line, left out. A link with curve natural: drawn, drawn, arrow lost, arrow. linkStyle 3 with one link: no diagram, no diagram, error, clear error. Source: our renders of the same sources with mermaid 11.17.2, 12.3.0 and @excalidraw/mermaid-to-excalidraw 2.2.2 and 3.6.0.

From Mermaid to Excalidraw shapes

Excalidraw’s “Mermaid to Excalidraw” turns a flowchart into shapes you can drag and restyle. It is a library, mermaid-to-excalidraw, and it changed underneath in October 2026: version 3.0 stopped using Mermaid. Its README now says diagrams “are parsed and laid out by Pele”, a separate Mermaid renderer published in Obsidian’s GitHub organisation, and warns that converted diagrams look different from v2 as a result.

We sent 97 of our test flowcharts (all but the 50,000-character one) through 2.2.2 (still the version in the @excalidraw/excalidraw 0.18.1 npm package) and 3.6.0 (4 November 2026, already in Excalidraw’s main branch):

  • Subgraphs and swimlanes: 2.2.2 turned all 10 of our flowcharts with subgraphs or lanes into one picture. The nine with subgraphs logged SubGraph element not found: the converter looks up each group by its plain id, and current Mermaid prefixes ids with the diagram’s own id. It is reported in issue #107, still open. swimlane-beta is a type 2.2.2 does not convert at all. 3.6.0 turned all 10 into boxes, lanes and arrows.
  • Named shapes (doc, cyl, person …) became plain rectangles in both. The 14 bracket shapes keep their form where Excalidraw has one: circles, the diamond.
  • Invisible links ~~~ became a visible line in 2.2.2; 3.6.0 leaves them out.
  • A link with curve: natural disappeared in 2.2.2 (two links in, one arrow out); 3.6.0 kept it.
  • Errors are Mermaid’s in 2.2.2. In 3.6.0 they come from Pele and read better: the linkStyle trap says The index 3 for linkStyle is out of bounds.

Mermaid flowcharts in Jira and Confluence

Jira and Confluence show a mermaid code block as code. To see the flowchart, you need an app; all 16 Mermaid apps for Jira are compared in Mermaid in Jira, and Confluence options in Mermaid in Confluence. What ours do with the syntax on this page:

  • Mermaid Tabs for Jira draws the diagram in a tab on the work item with Mermaid 12.1.0 and the classic look, so you get ELK layout with the familiar boxes. swimlane-beta works there; the step animation needs 12.3.0 and does not.
  • Our Excalidraw apps for Confluence and Jira put a flowchart on a whiteboard in two ways. Insert draws it with Mermaid 11.17.2 and keeps the source, so you can change the code later. Insert as shapes uses mermaid-to-excalidraw 2.2.2, so today a flowchart with subgraphs or lanes arrives as one picture, as described above. Use Insert for grouped flowcharts until the apps move to the 3.x converter.

For sequence, class and state diagrams, see the Mermaid UML cheat sheet, and for how Mermaid compares with PlantUML, D2 and Graphviz on the same six diagrams, see Mermaid vs PlantUML.

Sources

  • Mermaid flowchart docs at the tested tags: 12.3.0 and 11.17.2.
  • Mermaid’s config schema (defaults for wrappingWidth, minNodeWidth, curve, stepNumbers) and shape registry (rendering-elements/shapes.ts) at the same tags, in the mermaid-js/mermaid repository.
  • Swimlanes docs, Mermaid 12.3.0.
  • mermaid-to-excalidraw: README of 3.6.0 and issue #107.
  • Our renders: 98 flowcharts in Mermaid 11.17.2 and 12.3.0 and two converter versions, headless Chromium, 11 November 2026, no network requests during the run.

Frequently asked questions

Is Mermaid flowchart free?

Yes. Mermaid is open source under the MIT licence, so the library, the syntax and the official live editor cost nothing. Paid products are built on top of it, such as hosted editors and apps for Jira or Confluence, but the flowchart itself needs none of them.

How do I make a Mermaid flowchart horizontal?

Write flowchart LR on the first line for left to right, or flowchart RL for right to left. TD and TB both mean top to bottom and gave identical drawings in our test. The direction must be in capitals: flowchart lr is a lexical error in Mermaid 11 and 12.

Can ChatGPT or another AI write a Mermaid flowchart?

Yes, language models write Mermaid well because it is plain text. Check what they give you against the traps on this page: lowercase directions, a node called end, brackets inside labels and linkStyle numbers that point past the last link are the mistakes we saw break a whole diagram.

How do I add a line break inside a Mermaid flowchart node?

Put <br> in the label, for example A[First line<br>second line]. Or use a Markdown string, the label in backticks inside quotes, and press Enter where the new line should start. Long labels also wrap on their own: after 120 px of text in Mermaid 12, after 200 px in Mermaid 11.

What is the difference between graph and flowchart in Mermaid?

graph is the older keyword and flowchart the current one. Both drew the same diagram in our test, in Mermaid 11.17.2 and 12.3.0. The docs and new features use flowchart, so write that.

How do I change the colour of one node in a Mermaid flowchart?

Add style A fill:#fde68a,stroke:#b45309 on its own line, or define a class once with classDef warn fill:#fde68a and attach it with A:::warn or class A,B warn. Check the id: style on an id that is not in the diagram does not fail, it adds a new coloured box.

Why does my Mermaid flowchart say Parse error?

Usually one of four things: a node called end in lowercase, unquoted brackets inside a label such as A[Call f(x)], a reversed arrow such as B <-- A, or a subgraph name with a space and no id. Each has a fix in the traps table on this page.

Keep reading