Mermaid ER diagram editor
An entity-relationship diagram shows tables, their columns, and how they join. Use one when the subject is a database schema and the interesting questions are about keys and cardinality — one-to-many, optional, or through a join table. For types with behaviour and inheritance, use a class diagram instead.
A normalised order schema with a join table
The `}o--||` on ORDER_ITEM is the part worth studying: it is how you express a many-to-many relationship honestly, through a join table with its own columns, rather than pretending two tables connect directly.
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT ||--o{ ORDER_ITEM : "appears in"
CUSTOMER ||--o{ ADDRESS : "ships to"
CUSTOMER {
int id PK
string email UK "lowercased on write"
string name
datetime created_at
}
ORDER {
int id PK
int customer_id FK
string status
decimal total_amount
}
ORDER_ITEM {
int order_id PK, FK
int product_id PK, FK
int quantity
decimal unit_price "price at time of order"
}
PRODUCT {
int id PK
string sku UK
string name
}
ADDRESS {
int id PK
int customer_id FK
string postcode
}Worked examples
1. Two entities and one relationship
Every ER relationship needs a label after the colon — it is not optional, unlike a flowchart edge. Read it as a sentence: CUSTOMER places ORDER.
erDiagram
CUSTOMER ||--o{ ORDER : places2. Adding columns
Each attribute line is `type name`, and both parts are required. `PK`, `FK` and `UK` mark keys; a quoted string at the end is a comment.
erDiagram
CUSTOMER ||--o{ ORDER : places
CUSTOMER {
int id PK
string email UK
string name
}
ORDER {
int id PK
int customer_id FK
datetime placed_at "UTC"
}3. Cardinality that says something
The two characters nearest each entity are its cardinality. This example says an order must have at least one line, but a customer may have no orders at all — a real constraint that the schema should enforce and the diagram should show.
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_LINE : contains
ORDER_LINE }o--|| PRODUCT : references4. Many-to-many through a join table
Mermaid can draw a direct many-to-many, but a schema rarely has one — there is a join table underneath. Drawing the join table is more honest and gives you somewhere to put the columns that belong to the relationship itself.
erDiagram
STUDENT ||--o{ ENROLMENT : "signs up for"
COURSE ||--o{ ENROLMENT : "is taken via"
ENROLMENT {
int student_id PK, FK
int course_id PK, FK
datetime enrolled_at
string grade "null until marked"
}
STUDENT {
int id PK
string name
}
COURSE {
int id PK
string code UK
}5. Self-referencing relationships
A table can relate to itself — a reporting line, a category tree, a threaded comment. The relationship label matters more than usual here, because both ends are the same entity.
erDiagram
EMPLOYEE ||--o{ EMPLOYEE : manages
EMPLOYEE {
int id PK
int manager_id FK "null for the CEO"
string name
string job_title
}ER diagram syntax reference
Cardinality is written as two characters at each end of the relationship, and it reads outward from the middle. The left pair describes the left entity; the right pair describes the right entity.
| Syntax | Meaning |
|---|---|
| erDiagram | Opens the diagram. Case-sensitive. |
| A ||--o{ B : label | Relationship. The label is required. |
| || | Exactly one. |
| o| | Zero or one. |
| }| | One or more. |
| }o | Zero or more. |
| -- | Identifying relationship — solid line. |
| .. | Non-identifying relationship — dashed line. |
| A { ... } | Attribute block for entity A. |
| int id PK | Attribute: type, then name, then optional key marker. |
| PK / FK / UK | Primary, foreign, unique key. |
| int id PK, FK | Multiple key markers, comma-separated. |
| string email "comment" | Trailing quoted string is a comment on the column. |
| A ||--o{ B : "two words" | Quotes are REQUIRED for any label containing a space. Without them the label is cut at the first space and each remaining word becomes a phantom entity. |
Errors that break ER diagrams
ER diagrams have the strictest grammar of the six types here — the things that render fine elsewhere will be rejected outright. All reproduced against Mermaid 11.12.2.
What you see
Parse error, ending in: Expecting 'COLON', 'STYLE_SEPARATOR', got 'NEWLINE'
Why
A relationship with no label. Unlike a flowchart edge, the colon and a label are mandatory — the line simply ends too early without them.
Fix
Add a colon and a verb phrase. Keep it short enough to read as a sentence.
erDiagram
CUSTOMER ||--o{ ORDERerDiagram
CUSTOMER ||--o{ ORDER : placesWhat you see
Parse error, ending in: got 'UNICODE_TEXT'
Why
An invalid cardinality token. Only `||`, `o|`, `}|` and `}o` (and their mirrors) are valid; anything else fails. `oo` is the usual typo, from expecting the notation to be symmetrical.
Fix
Use one of the four pairs. `||--o{` — exactly one to zero-or-more — covers most foreign keys.
erDiagram
CUSTOMER ||--oo{ ORDER : placeserDiagram
CUSTOMER ||--o{ ORDER : placesWhat you see
Parse error, ending in: Expecting 'ATTRIBUTE_WORD', got 'BLOCK_STOP'
Why
An attribute with a name but no type. ER attributes are `type name`, and the type is not optional — which surprises people coming from schemaless databases.
Fix
Give every attribute a type. Invent one if the real schema has none: `string`, `int`, `json`.
erDiagram
CUSTOMER {
email
}erDiagram
CUSTOMER {
string email
}What you see
No diagram type detected matching given configuration
Why
Wrong capitalisation. `erdiagram` and `ERDiagram` both fail; only `erDiagram` works.
Fix
Lowercase e, capital D.
erdiagram
A ||--o{ B : haserDiagram
A ||--o{ B : hasWhat you see
Parse error, ending in: Expecting 'NON_IDENTIFYING', 'IDENTIFYING', got 'UNICODE_TEXT'
Why
The line between the two cardinality markers is wrong. It must be exactly two characters — `--` for an identifying relationship or `..` for a non-identifying one. A single dash is not a shorter version of `--`, it is a syntax error.
Fix
Use `--` or `..` between the cardinality pairs.
erDiagram
CUSTOMER ||-o{ ORDER : placeserDiagram
CUSTOMER ||--o{ ORDER : placesWhat you see
The cardinality renders but describes the opposite constraint
Why
The two characters nearest each entity belong to that entity, and it is easy to read them the wrong way round. `CUSTOMER ||--o{ ORDER` says one customer has zero or more orders. Flip it to `CUSTOMER }o--|| ORDER` and you have said every customer belongs to exactly one order, which is nonsense — and it renders without complaint.
Fix
Read outward from the middle: the marks touching CUSTOMER describe how many customers, not how many orders.
erDiagram
CUSTOMER }o--|| ORDER : placeserDiagram
CUSTOMER ||--o{ ORDER : placesRendering notes
Measured against Mermaid 11.12.2 as this site runs it.
Layout is driven by relationships, not by entity count
A chain of entities each related to the next renders as a tall column — forty entities produce a viewBox around 116×7500 — but the same forty entities related to one central table produce something much wider and shorter. This is the one diagram type here where the shape of your data changes the shape of the picture more than its size does, so a schema that will not fit is often fixed by changing which relationships you draw rather than how many.
The grammar is the strictest of the six types
Relationship labels are mandatory, attribute types are mandatory, and cardinality tokens are a closed set. Compared with Gantt, where almost everything renders and mistakes are silent, ER diagrams fail loudly and early. That is a feature: if an ER diagram renders, it is far more likely to be the diagram you meant.
Labels are HTML, so PNG export re-renders
Entity attribute tables are drawn inside an SVG `<foreignObject>`, so browsers cannot rasterise them to a canvas directly. PNG export on this site used to silently return an SVG file; it now re-renders with plain SVG text labels first, producing a correct full-size PNG with marginally different typography.
Entity names are case-sensitive and conventionally uppercase
`CUSTOMER` and `customer` are two different entities, and referring to both in the same diagram silently gives you two boxes. The uppercase convention is not enforced by Mermaid but is worth keeping precisely because it makes an accidental lowercase reference obvious.
Theme changes colour, never layout
Default and dark themes produce an identical viewBox for the same source, so attribute tables cannot reflow or clip when the theme changes.
When to use something else
If you need inheritance, interfaces or methods, this is the wrong diagram — ER has no concept of any of them. Use a class diagram, and accept that it models your code rather than your tables.
If the schema is large, an ER diagram of the whole thing is a wall chart nobody reads. Draw the five tables involved in the thing you are explaining and leave the rest out; a diagram is an argument, not an inventory.
And if the real question is how data moves between systems rather than how it is stored, a flowchart with subgraphs per system will answer it and an ER diagram will not.
Other diagram types
Written by Dominik Malsch · Last updated: