Mermaid ER Diagrams: Syntax, Examples and Limitations

Mermaid lets you describe an entity-relationship diagram in a few lines of text inside a Markdown file, and GitHub, GitLab and most documentation generators render it for you. It is the quickest way to put a small schema picture in a README. This guide covers the erDiagram syntax with a working example, the cardinality symbols, where the diagrams render, and the limits you will hit as the schema grows.

A first diagram#

An erDiagram block lists relationships and, optionally, the attributes of each entity. This is the PetClinic schema: owners with pets, pets with a type and visits, vets with specialties.

erDiagram
    owners ||--o{ pets : owns
    types ||--o{ pets : classifies
    pets ||--o{ visits : receives
    vets ||--o{ vet_specialties : has
    specialties ||--o{ vet_specialties : has

    owners {
        int id PK
        varchar first_name
        varchar last_name
        varchar address
        varchar city
        varchar telephone
    }
    pets {
        int id PK
        varchar name
        date birth_date
        int type_id FK
        int owner_id FK
    }
    visits {
        int id PK
        int pet_id FK
        date visit_date
        varchar description
    }

In a Markdown file the block goes inside a fenced code block whose language is mermaid. Entities that appear only in relationship lines are drawn as empty boxes; entities with an attribute block are drawn with their columns. Entity names may be in any case; upper case is a convention from the modeling literature, not a requirement.

Relationships and cardinality#

A relationship line has the form left symbol--symbol right : label. The label is required (it may be an empty string in quotes), and each end carries a two-character symbol that encodes cardinality and optionality.

Left endRight endMeaning
|oo|Zero or one
||||Exactly one
}oo{Zero or more
}||{One or more

So owners ||--o{ pets reads "one owner, zero or more pets", which is the usual one-to-many. A many-to-many between vets and specialties is written through the join table, as two one-to-many lines, exactly as it exists in the database; you can also write vets }o--o{ specialties : has when you want the conceptual view without the join table. Two dashes, --, draw a solid line for an identifying relationship (the child cannot exist without the parent); two dots, .., draw a dashed line for a non-identifying one. Most generated diagrams use solid lines throughout, and nobody will complain if you do the same.

Attributes, keys and comments#

Each attribute line is type name, optionally followed by a key marker and a quoted comment.

    vet_specialties {
        int vet_id PK, FK "part of the composite key"
        int specialty_id PK, FK
    }
    inventory {
        bigint product_id PK, FK "one row per product"
        int quantity
        timestamptz updated_at
    }

The markers are PK, FK and UK (unique), and a column may carry several separated by commas. Types are free text, so you can write the engine's names (int4, varchar2) or logical ones. Mermaid does not validate that an FK attribute matches a relationship line; the two are independent, which is convenient when writing and a source of inconsistency when maintaining.

Where Mermaid renders#

  • GitHub renders mermaid code blocks in Markdown files, issues, pull requests and discussions.
  • GitLab renders them in Markdown files, issues and wikis.
  • Documentation generators such as MkDocs, Docusaurus, VitePress and Hugo render them through a plugin or theme option.
  • Editors including VS Code, Obsidian and JetBrains IDEs preview them with a plugin, and the Mermaid Live Editor renders and exports any diagram in the browser.

Rendering happens in the reader's browser, so the diagram is always laid out by the Mermaid version the platform ships. Two platforms can draw the same text differently, and an upgrade can rearrange a diagram you had grown used to. The official syntax reference tracks what the current version supports.

Limitations#

Layout is automatic and mostly out of your hands#

Mermaid lays the diagram out itself. You can influence the result by the order of statements, and little else: you cannot pin an entity to a position or route a line around another. For a dozen entities the result is fine; beyond that, lines cross and boxes wander.

Large schemas do not fit#

An erDiagram with fifty entities renders, technically, as a very wide picture that nobody can read. The workable approach is one diagram per subject area, each with five to fifteen entities, which means several blocks to maintain.

It is text you maintain by hand#

Nothing connects the Mermaid block to the migrations. Every added column and every new foreign key has to be typed into the block, and the diagram silently drifts when someone forgets. The same problem affects every hand-drawn diagram; Mermaid only makes the drawing cheaper, not the upkeep. Keeping a database diagram up to date with CI describes the alternative.

It shows the logical view only#

There is no notion of indexes, nullability beyond what you write in a comment, check constraints or defaults. That is appropriate for an overview and insufficient as the only schema documentation.

Generating Mermaid instead of writing it#

The drift problem disappears when the block is generated from the schema. tbls, which connects to a database and writes Markdown documentation, can emit its diagrams as Mermaid with --er-format mermaid, and there are generators for specific ORMs, such as the ERD generator for Prisma schemas. The pattern is the one used for any generated documentation: run the migrations against a throw-away database in CI, generate the text, commit it. The Mermaid block then becomes an output, and the README shows a picture that is right by construction.

When you generate, prefer one block per subject area and keep them in separate files, so that a change to one area produces a small diff.

Where SchemaDrawer fits#

SchemaDrawer solves the same upkeep problem from the other end: instead of maintaining a text description, it runs your migration files in Docker against a real PostgreSQL, MySQL, MariaDB, SQL Server, Oracle or SQLite of the version you choose, and returns an SVG diagram of the resulting tables, columns, primary keys and foreign keys. It outputs SVG rather than Mermaid, so it suits a committed image file or a docs page rather than an inline Markdown block; the two approaches complement each other, Mermaid for the small hand-written overview, a generated SVG for the full, current schema. The examples show what the output looks like, and the introduction to ER diagrams covers the notation both use.

Key takeaways#

  • An erDiagram block needs only relationship lines; attribute blocks add columns with PK, FK and UK markers.
  • Cardinality lives in the two-character end symbols: || exactly one, o{ zero or more, and so on.
  • Mermaid renders on GitHub, GitLab and most docs generators, with layout decided by the platform.
  • It works for small diagrams; large schemas need several blocks, and hand-written blocks drift unless generated.
Marek Hudyma
Builds SchemaDrawer, a service that draws database diagrams by running your migrations on a real engine. About

Draw your schema

Upload your migration files, pick the engine version you deploy to, get an SVG back. Free while in early access.

Get started
Keep reading

Related articles

12 Tools to Visualize a Database Schema, Compared

From dbdiagram.io and DBeaver to SchemaSpy, tbls and Mermaid: what each schema visualization tool takes as input, what it produces and when to use it.

How to Generate an ER Diagram from Migration Files

Three ways to turn Flyway, Liquibase, Prisma or Django migrations into an ER diagram, and a step-by-step guide to the one that always matches reality.

Draw your schema in the next five minutes

One HTTP call with your migration files, one poll, one SVG. Free while we build it.