Skip to main content

🧜‍♀️ Learning Mermaid: A Complete Tutorial for Diagram Creation

A picture is worth a thousand words, but drawing one in a graphics editor is a thousand clicks. Mermaid flips that: you write a few lines of plain text and it renders a clean diagram for you. Because the source is text, your diagrams live in Git right next to the code they describe — versioned, reviewable, and easy to change. This tutorial walks through every major diagram type, and every example here actually renders.

Reference & Extra Tutorials · Resources · Diagrams as Code

🎯 What This Covers

By the end of this reference, you will be able to:

  • Explain what Mermaid is and add it to any HTML page or Markdown doc
  • Build flowcharts with the full range of node shapes and directions
  • Write sequence diagrams for API and service interactions
  • Model object structure with class diagrams and databases with ER diagrams
  • Capture system behavior with state diagrams and history with gitGraph
  • Avoid the handful of syntax traps that silently break a diagram

Estimated Time: 55 minutes

Practice: Diagram a small app end to end — a flowchart, a sequence diagram, and an ER diagram.

In This Tutorial

What Is Mermaid?

Mermaid is a JavaScript library that turns Markdown-inspired text into diagrams. You describe the nodes (the boxes) and the relationships (the arrows) in a simple syntax, and Mermaid draws and lays them out automatically. It's the same idea as Markdown itself: write plain text, get a formatted result — except the result is a chart instead of a heading.

Think of Mermaid syntax like a recipe. A recipe lists ingredients and how they combine; a Mermaid diagram lists elements and how they connect. The first word — flowchart, sequenceDiagram, erDiagram — is you choosing which dish you're making, and each has its own rules.

Here is the smallest possible flowchart. This one renders:

graph TD A[Write text] --> B[Mermaid renders it] B --> C[You get a diagram]

💡 Why teams love diagrams-as-code

  • Version control friendly — the diagram is text, so Git tracks every change and shows clean diffs
  • Fast to edit — change a label by editing a word, no design tool needed
  • Lives with the code — GitHub, GitLab, and many docs tools render Mermaid inline in Markdown
  • Consistent — the layout engine keeps everything aligned automatically

Adding Mermaid to a Page

There are two common ways to get Mermaid running.

Option 1: The CDN (fastest to try)

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

Option 2: NPM (for real applications)

# Install it as a dependency
npm install mermaid
import mermaid from "mermaid";
mermaid.initialize({ startOnLoad: true });

Where the diagram goes

Put your diagram text inside an element with the class mermaid. On page load, Mermaid finds every such element and renders it.

<div class="mermaid">
    graph TD
        A[Start] --> B[Finish]
</div>

In Markdown files on GitHub or GitLab, you don't need any of the setup — just a fenced code block tagged mermaid:

```mermaid
graph LR
    A[Idea] --> B[Diagram]
```

📖 graph vs flowchart

You'll see both graph TD and flowchart TD in the wild. They're aliases for the same diagram; flowchart is the newer keyword. Either works. The letters after it set direction: TD/TB top-to-bottom, BT bottom-to-top, LR left-to-right, RL right-to-left.

Flowcharts

Flowcharts are the workhorse: processes, decision trees, architectures. A flowchart is just nodes joined by arrows.

Node shapes

The brackets around a node's text choose its shape:

flowchart TD
    A[Rectangle]
    B(Rounded)
    C([Stadium])
    D[[Subroutine]]
    E[(Database)]
    F((Circle))
    G{Decision}
    H{{Hexagon}}
flowchart TD A[Rectangle] B(Rounded) C([Stadium]) D[[Subroutine]] E[(Database)] F((Circle)) G{Decision} H{{Hexagon}}

Arrows and labels

flowchart LR
    A[Solid arrow] --> B[Node]
    C[Dotted] -.-> D[Node]
    E[Thick] ==> F[Node]
    G[Labeled] -->|yes| H[Node]
flowchart LR A[Solid arrow] --> B[Node] C[Dotted] -.-> D[Node] E[Thick] ==> F[Node] G[Labeled] -->|yes| H[Node]

Real example: a login flow

Decisions use the diamond shape {...}, and the arrow labels carry the branch conditions.

flowchart TD A[User visits site] --> B{Has account?} B -->|Yes| C[Login form] B -->|No| D[Registration form] C --> E{Valid credentials?} E -->|Yes| F[Dashboard] E -->|No| G[Show error] G --> C D --> H[Create account] H --> C

Grouping with subgraphs

A subgraph draws a labeled box around related nodes — great for showing layers or services.

flowchart LR subgraph Client A[Browser] --> B[App state] end subgraph Server C[API] --> D[(Database)] end B --> C

⚠️ Parentheses in labels must be quoted

If a node label contains parentheses, wrap the whole label in double quotes or the parser chokes. Write A["fs.readFile()"], never A[fs.readFile()]. The same goes for other special characters like colons.

flowchart LR A["readFile()"] --> B["parse(data)"] B --> C["render()"]

Sequence Diagrams

Sequence diagrams show messages passing between participants over time. They're ideal for API calls and service-to-service communication. Time flows downward; each arrow is a message.

Basic syntax

sequenceDiagram
    participant A as Alice
    participant B as Bob
    A->>B: Hello Bob how are you
    B->>A: I am good thanks
    A->>B: Great to hear
sequenceDiagram participant A as Alice participant B as Bob A->>B: Hello Bob how are you B->>A: I am good thanks A->>B: Great to hear

Real example: API authentication

alt and else draw a labeled box for a branch. Notice the message text is plain prose — that's a hard rule, explained below.

sequenceDiagram participant Client participant API participant Auth as Auth Service participant DB as Database Client->>API: Request with API key API->>Auth: Validate the key Auth->>DB: Look up the key DB->>Auth: Return key status alt Key is valid Auth->>API: Authentication succeeded API->>DB: Fetch requested data DB->>API: Return the data API->>Client: Respond 200 with data else Key is invalid Auth->>API: Authentication failed API->>Client: Respond 401 Unauthorized end

⚠️ Message text must be plain prose

Everything after the colon in a sequence message must avoid ;, parentheses (), ==, and ? — those characters confuse the parser and silently break the render. Write "Recompute the hash and compare", not "SHA256(x) == stored?". Rephrase into words and the diagram renders every time.

💡 The mailroom analogy

A sequence diagram is like tracking letters between mailboxes. Each participant is a mailbox, each arrow is a letter, and reading top to bottom replays the whole conversation in order — perfect for seeing exactly where a message got lost.

Class Diagrams

Class diagrams describe object-oriented structure: classes, their attributes and methods, and how they relate. They're the blueprint before the code.

Basic syntax

classDiagram
    class Animal {
        +String name
        +int age
        +makeSound() void
    }
    class Dog {
        +String breed
        +fetch() void
    }
    Animal <|-- Dog
classDiagram class Animal { +String name +int age +makeSound() void } class Dog { +String breed +fetch() void } Animal <|-- Dog

Relationship types

NotationMeaning
<|--Inheritance (is a)
*--Composition (owns, dies with)
o--Aggregation (has, outlives)
-->Association (uses)
..>Dependency

Real example: an e-commerce model

classDiagram class User { +String email +login() bool +logout() void } class Customer { +String shippingAddress +placeOrder() Order } class Order { +String orderNumber +String status +calculateTotal() float } class OrderItem { +int quantity +float unitPrice } class Product { +String name +float price } User <|-- Customer Customer "1" --> "*" Order : places Order "1" *-- "*" OrderItem : contains OrderItem "*" --> "1" Product : references

💡 The blueprint analogy

A class diagram is an architectural blueprint for software. Classes are rooms, attributes are the furniture, methods are what each room does, and the relationship lines are the hallways and doorways connecting them.

State Diagrams

State diagrams model the states a system can be in and the events that move it between them — perfect for order lifecycles, UI flows, and any finite-state machine. Use stateDiagram-v2; [*] marks the start and end.

Basic syntax

stateDiagram-v2
    [*] --> Idle
    Idle --> Processing: Start task
    Processing --> Complete: Task finished
    Processing --> Failed: Error thrown
    Complete --> [*]
    Failed --> Idle: Reset
stateDiagram-v2 [*] --> Idle Idle --> Processing: Start task Processing --> Complete: Task finished Processing --> Failed: Error thrown Complete --> [*] Failed --> Idle: Reset

Real example: an order's lifecycle

stateDiagram-v2 [*] --> Created Created --> Paid: Payment received Created --> Cancelled: Timeout Paid --> Preparing: Inventory checked Preparing --> Shipped: Order packaged Shipped --> Delivered: Order received Delivered --> [*] Cancelled --> [*] note right of Created: Awaiting payment note right of Shipped: En route to customer

💡 The water-phase analogy

Think of water as ice, liquid, or vapor. Each is a state; temperature changes are the events that trigger transitions. A state diagram captures exactly that — the possible states and what pushes the system from one to the next.

ER Diagrams

Entity-relationship diagrams describe a database's structure: the tables (entities), their columns (attributes), and how they relate. They're the plan you draw before writing a single CREATE TABLE.

Basic syntax

erDiagram
    CUSTOMER ||--o{ ORDER : places
    ORDER ||--|{ LINE_ITEM : contains
    CUSTOMER {
        string name
        string email
    }
    ORDER {
        int orderNumber
        date orderDate
    }
erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE_ITEM : contains CUSTOMER { string name string email } ORDER { int orderNumber date orderDate } LINE_ITEM { string product int quantity float price }

Reading the crow's-foot notation

NotationRelationship
||--||one to one
||--o{one to many (zero or more)
||--|{one to many (one or more)
}o--o{many to many

Real example: a blog database

erDiagram USER ||--o{ POST : writes USER ||--o{ COMMENT : makes POST ||--o{ COMMENT : has USER { int id PK string username string email } POST { int id PK int user_id FK string title datetime published_at } COMMENT { int id PK int post_id FK int user_id FK string content }

💡 The family-tree analogy

An ER diagram is a family tree for data. Entities are people, attributes are their traits, and the relationship lines show how they connect — one-to-one for spouses, one-to-many for a parent and their children.

Git Graphs

A gitGraph visualizes branches, commits, and merges — a great way to teach a branching strategy or document a release history.

gitGraph
    commit id: "init"
    branch feature
    checkout feature
    commit id: "add form"
    commit id: "add tests"
    checkout main
    merge feature
    commit id: "release"
gitGraph commit id: "init" branch feature checkout feature commit id: "add form" commit id: "add tests" checkout main merge feature commit id: "release"

📖 A quick tour of what you've seen

Flowchart, sequence, class, state, ER, and gitGraph cover the vast majority of everyday documentation needs. Mermaid also supports pie charts, Gantt charts, user-journey maps, mindmaps, and timelines — once you're comfortable with the six here, the rest follow the same "declare a type, then describe the parts" pattern.

Rules That Keep Diagrams Rendering

Most "my diagram is blank" problems come from a small set of syntax traps. Internalize these five and your diagrams will render the first time.

1. Quote labels with parentheses or special characters

A["parse(input)"] renders; A[parse(input)] breaks. Wrap any label containing (), :, or other punctuation in double quotes.

2. Never name a node end

Lowercase end is a reserved keyword in flowcharts. A node called end can collapse the whole diagram. Use done, finish, or Endpoint instead.

3. Keep sequence messages as plain prose

After the colon in a sequenceDiagram message, avoid ;, (), ==, and ?. "Validate the token and respond" renders; "validate(token) == ok?" does not.

4. Mind the diagram-type keyword

The first line declares the type: flowchart/graph, sequenceDiagram, classDiagram, stateDiagram-v2, erDiagram, gitGraph. A typo here means nothing renders. Match the syntax to the type you declared.

5. Don't hard-code colors that fight the theme

Avoid style X fill:#f96 and classDef color directives. They look fine in light mode and become unreadable in dark mode. Leave nodes unstyled and let the theme control them — every diagram on this page does exactly that.

When a diagram won't render, paste it into the Mermaid Live Editor. It shows the exact parse error and line number, which turns a mysterious blank box into a one-minute fix.

Practice & Quiz

🏋️ Exercise 1: Fix the broken flowchart

Goal: This flowchart refuses to render. Find and fix the two problems.

flowchart TD
    start[Begin] --> load[readFile(config)]
    load --> end[Stop]
💡 Hint

One node has an unquoted label with parentheses. Another node uses a reserved keyword for its id.

✅ Solution
flowchart TD
    start[Begin] --> load["readFile(config)"]
    load --> done[Stop]
flowchart TD start[Begin] --> load["readFile(config)"] load --> done[Stop]

Quoting "readFile(config)" fixes the parentheses; renaming end to done frees the reserved word.

🏋️ Exercise 2: Diagram a login sequence

Goal: Write a sequence diagram with three participants (User, Frontend, Backend) showing a successful login and a failed one, using alt/else. Keep all message text as plain prose.

✅ Solution
sequenceDiagram
    participant User
    participant FE as Frontend
    participant BE as Backend
    User->>FE: Enter credentials
    FE->>BE: Submit login request
    alt Credentials are valid
        BE->>FE: Return session token
        FE->>User: Show dashboard
    else Credentials are invalid
        BE->>FE: Return an error
        FE->>User: Show error message
    end
sequenceDiagram participant User participant FE as Frontend participant BE as Backend User->>FE: Enter credentials FE->>BE: Submit login request alt Credentials are valid BE->>FE: Return session token FE->>User: Show dashboard else Credentials are invalid BE->>FE: Return an error FE->>User: Show error message end

🎯 Quick Quiz

Question 1: Why store diagrams as Mermaid text instead of image files?

Question 2: Which flowchart node label will break the render?

Question 3: What's wrong with this sequence message: A->>B: verify(token) == ok?

Best Practices & Pitfalls

✅ Do

  • Keep each diagram focused — split a sprawling one into several smaller views
  • Quote any label containing parentheses, colons, or other punctuation
  • Use consistent, descriptive node ids and labels
  • Store diagrams next to the code they describe so they stay in sync
  • Test a tricky diagram in the Mermaid Live Editor before committing

❌ Don't

  • Name a flowchart node end — it's reserved and can blank the diagram
  • Put ;, (), ==, or ? in sequence-diagram message text
  • Hard-code fill:#hex colors that become unreadable in dark mode
  • Cram thirty nodes into one chart — nobody can read it
  • Let diagrams rot — update them when the system changes

✅ Living documentation

The whole point of diagrams-as-code is that they can stay true. Put the Mermaid source in the same pull request as the code change it describes, and reviewers will keep both honest together.

Summary

🎉 Key Takeaways

  • Mermaid turns plain text into diagrams that live in version control
  • The pattern is always the same: declare a type, then describe nodes and relationships
  • You now have six workhorses: flowchart, sequence, class, state, ER, and gitGraph
  • The render-breaking traps are few: quote parenthesized labels, avoid the end keyword, keep sequence messages prose-only, and skip hard-coded colors
  • When in doubt, the Mermaid Live Editor shows the exact error

📚 Additional Resources

🚀 What's Next?

You've been rendering diagrams in the browser; next you'll master the environment where you'll run most of your backend tools. Up next: the Ubuntu Command Line Interface — navigating, managing files, permissions, pipes, and enough shell to be dangerous in the best way.

🎉 You speak Mermaid now!

From here on, any system you build can document itself. A few lines of text, and your architecture is a picture your whole team can read.