hasarakMinimalModern

Mermaid diagrams in team docs: a practical guide

Diagrams-as-code that live next to the text, review like code and never go stale in a drawing tool. Five copy-paste examples, where Mermaid renders, and the mistakes that break a diagram.

· 8 min read

Most diagrams in team docs die the same way: someone draws them in a drawing tool, exports a PNG, pastes it into a doc, and six months later the architecture has moved on and nobody can find the source file.

Mermaid fixes the part that matters: the diagram is text inside the Markdown, in a fenced code block. It changes in the same edit as the paragraph above it, shows up in the diff when someone reviews the change, and renders wherever the doc is read, as long as the renderer supports Mermaid.

This guide is for engineers writing design docs, runbooks and READMEs. It covers the five diagram types that carry most team docs, with source you can copy, the rendered result, and the syntax traps that produce “Syntax error in text” at the worst moment.

How Mermaid works in Markdown

You write a fenced code block with the language set to mermaid:

```mermaid
flowchart LR
  a[Write the doc] --> b[Open a PR] --> c[Review the diagram as text]
```

A renderer that understands Mermaid replaces the block with an SVG. One that doesn’t shows the source as a code block, which is still readable: that graceful fallback is a big part of why Mermaid works for docs.

The first line names the diagram type (flowchart, sequenceDiagram, stateDiagram-v2, erDiagram, gantt, and many more: class, mindmap, timeline, gitGraph, C4, Sankey, XY charts, architecture and others). This guide uses Mermaid 12.2, the current release. The rest is that type’s syntax.

Where Mermaid renders

Support varies, and it’s the first thing to check before you standardise on Mermaid for a team.

Where Renders ```mermaid blocks?
GitHub (Markdown files, issues, PRs, discussions, wikis) Yes, since February 2022
GitLab Yes (docs)
Azure DevOps wiki Yes, with ::: mermaid blocks, for a subset of diagram types; write graph, not flowchart (docs)
Obsidian Yes (docs)
Notion Yes, a code block set to Mermaid, with preview (since December 2021)
Docusaurus With @docusaurus/theme-mermaid
Confluence Through Marketplace apps, not natively
Slack and Discord messages Not that we could find documented; the source shows as code
Hasarak pages Yes, in the web app with mermaid.js; the terminal and desktop apps render flowchart and sequenceDiagram

Renderers also differ in version. Mermaid adds diagram types and syntax regularly, and a platform pinned to an older release will fail on newer syntax. If a diagram works in the Mermaid Live Editor but not in your wiki, a version gap is the usual reason.

Example 1: a flowchart for a process

Flowcharts are the workhorse: deploy pipelines, decision trees, request routing, on-call escalation.

flowchart TD
  pr[Pull request] --> ci{CI green?}
  ci -- no --> fix[Fix and push]
  fix --> ci
  ci -- yes --> review{Approved?}
  review -- changes requested --> fix
  review -- yes --> merge[Merge to main]
  merge --> deploy([Deploy])
Flowchart: pull request, CI check, fix loop, review, merge and deploy
The flowchart above, rendered

What to know:

  • TD lays it out top-down; LR left to right. Pick the one that matches the doc’s width: left-to-right flows get cramped on a phone.
  • Shapes come from the brackets: [box], {decision}, ([stadium]), ((circle)), [(database)].
  • -- label --> puts text on an edge. Keep edge labels to a word or two.
  • Node ids (pr, ci) are separate from labels, so you can reword a label without touching the edges.

Example 2: a sequence diagram for a protocol

Sequence diagrams are the best tool for anything with a request and a response: auth flows, webhooks, retries. This one is the OAuth sign-in an MCP client does against a chat server (the flow from the MCP guide):

sequenceDiagram
  autonumber
  participant C as MCP client
  participant S as Chat server
  participant B as Browser
  C->>S: POST /mcp
  S-->>C: 401 + resource metadata URL
  C->>S: POST /oauth/register
  S-->>C: client_id
  C->>B: open /oauth/authorize (PKCE)
  B->>S: sign in and approve
  S-->>C: redirect with code
  C->>S: POST /oauth/token (code + verifier)
  S-->>C: access token + refresh token
  C->>S: POST /mcp (Bearer token)
Sequence diagram of an MCP client's OAuth sign-in: 401, dynamic registration, authorize with PKCE, token exchange, authenticated request
The sequence diagram above, rendered

What to know:

  • ->> is a solid arrow (a call), -->> a dashed one (a response).
  • autonumber numbers the steps, which makes the diagram easy to refer to in review comments (“step 7 should also send state”).
  • participant X as Long name keeps the source short and the picture readable.
  • alt/else/end, opt, loop and par blocks show branches and repetition; Note over C,S: text adds a note across participants.

Example 3: a state diagram for a lifecycle

Anything with statuses (incidents, orders, feature flags, PR states) reads better as a state diagram than as a bulleted list:

stateDiagram-v2
  [*] --> Investigating: alert fires
  Investigating --> Identified: cause found
  Identified --> Monitoring: fix deployed
  Monitoring --> Investigating: it came back
  Monitoring --> Resolved: quiet for 30 min
  Resolved --> [*]
State diagram of an incident: investigating, identified, monitoring, resolved, with a path back to investigating
The state diagram above, rendered

Use stateDiagram-v2, not stateDiagram: the v2 renderer is the current one. [*] is the start or end state. This exact diagram is a good header for an incident runbook; see running incidents in chat.

Example 4: an ER diagram for a schema

When a design doc changes tables, an ER diagram in the doc beats a screenshot of a database GUI:

erDiagram
  WORKSPACE ||--o{ CHANNEL : has
  CHANNEL ||--o{ MESSAGE : contains
  MESSAGE ||--o{ MESSAGE : "replies to"
  PERSON ||--o{ MESSAGE : writes
  WORKSPACE ||--o{ PAGE : has
  MESSAGE {
    string id PK
    string channel_id FK
    string parent_id FK
    string author_id FK
    text body
  }
ER diagram: workspace has channels and pages, channels contain messages, messages reply to messages, people write messages
The ER diagram above, rendered

The relationship syntax is crow’s foot notation written in ASCII: || is exactly one, o{ zero or more, |{ one or more. Quote a label that has spaces ("replies to").

Example 5: a Gantt chart for a plan

Gantt charts in docs are best for rough plans that change, where a dedicated project tool would be overkill:

gantt
  title Release 2.4
  dateFormat YYYY-MM-DD
  section Build
    API changes        :a1, 2026-11-02, 5d
    Web client         :a2, after a1, 6d
  section Ship
    Beta               :b1, after a2, 4d
    General release    :milestone, after b1, 0d
Gantt chart: API changes, then web client, then beta, then general release milestone
The Gantt chart above, rendered

after a1 chains tasks, so moving the first date moves everything after it. That’s the main reason to use Mermaid here instead of a table of dates.

The mistakes that break diagrams

Most “Syntax error in text” messages come from a short list:

  1. A node called end. In flowcharts, lowercase end closes a subgraph, so a --> end fails. Write End, END or done[end].
  2. Special characters in labels. Parentheses, brackets, colons, quotes and # confuse the parser. Wrap the label in double quotes: a["POST /oauth/token (PKCE)"], and use entity codes such as #quot; for a literal quote.
  3. A node id starting with o or x after --. a---oB is read as a circle-ended edge to B. Add a space or rename the node.
  4. Tabs and stray indentation in Gantt charts and ER attribute blocks. Use spaces.
  5. Newer syntax on an older renderer. Check what version your platform runs before using a recently added diagram type.
  6. Interactivity that’s switched off. With securityLevel: "strict", Mermaid’s default, click callbacks are disabled and HTML tags in labels are escaped. Platforms rendering user content keep it strict, so don’t rely on either in shared docs.

When a diagram fails, paste it into mermaid.live, which shows the error with a line number.

Habits that keep diagrams useful

  • One idea per diagram. If it needs a legend, split it.
  • Name nodes after the code. Use the service, table or function names people will search for.
  • Keep it under about 15 nodes. Past that, layouts get hard to read on a laptop and impossible on a phone.
  • Put the diagram next to the text it explains, not in an appendix. The point of diagrams-as-code is that they change in the same edit.
  • Review the source, not the picture. A PR diff of a Mermaid block shows exactly which edge changed.
  • Don’t hand-tune layout. Mermaid does automatic layout; fighting it with invisible nodes makes the source unreadable. If layout really matters, it’s a drawing, not a diagram-as-code.

Mermaid in Hasarak pages

Hasarak workspaces have pages: Markdown docs that live next to the channels and threads that discuss them (g d opens them). A ```mermaid block in a page renders as a diagram:

  • In the web app, with mermaid.js (in strict security mode).
  • In the terminal and desktop apps, with a Rust renderer that supports flowchart and sequenceDiagram and draws the same picture in every client. The terminal app shows it through the Kitty graphics protocol in Kitty and Ghostty. Other diagram types show their source. (Those apps have no public release yet; the web app is the shipped client.)

Pages can also be written by AI agents through the workspace’s MCP server: tools.hasarak.pages.create takes a Markdown body, and its description tells the agent that Mermaid blocks render. Asking an agent to “turn this thread into a page with a sequence diagram of the flow” is one of the more useful things it can do there. See chat with docs for how pages fit into the workspace.

Further reading

Team chat for people who live on the keyboard.

Channels, threads, an inbox and Markdown pages, with an MCP server and open connectors.

Start free