Skip to content

Mermaid

If a diagram already exists as Mermaid, diago draws it as it is:

Terminal window
diago render diagram.mmd > diagram.svg
diago render -format text diagram.mmd

Every verb reads Mermaid: render, check, both arguments of diff and -previous recognize a .mmd or .mermaid file, or a body whose first meaningful line is a Mermaid header, and translate it before anything else. diago import writes the translated JSON spec to stdout, for a diagram you want to keep editing in diago’s own terms: it gives you ids to keep and fields to set.

Terminal window
diago import diagram.mmd > spec.json
diago import < diagram.mmd | diago check

The dialect is read from the header. Three are supported: flowchart (or graph), sequenceDiagram and classDiagram.

The importer does not guess silently. A construct diago cannot carry is translated to its nearest form and reported on stderr, one line each, as warning: import line <n>: <message>, and the import still succeeds. A statement with no meaning in diago fails with diago <verb>: line <n>: <message> and exit 1, as does an input that is not Mermaid. The three sources below import with nothing on stderr.

flowchart.mmd
flowchart LR
client(Client) --> gateway[API Gateway]
subgraph backend [Backend]
gateway -->|route| orders[Orders]
orders -->|save| db[(Postgres)]
end
orders -.->|notify| mail{Send mail?}
mail ==>|yes| smtp[SMTP Relay]
flowchart.json
{
"type": "flow",
"direction": "RIGHT",
"nodes": [
{"id": "client", "label": "Client", "shape": "rounded"},
{"id": "gateway", "label": "API Gateway"},
{"id": "orders", "label": "Orders"},
{"id": "db", "label": "Postgres", "shape": "cylinder"},
{"id": "mail", "label": "Send mail?", "shape": "diamond"},
{"id": "smtp", "label": "SMTP Relay"}
],
"edges": [
{"from": "client", "to": "gateway"},
{"from": "gateway", "to": "orders", "label": "route"},
{"from": "orders", "to": "db", "label": "save"},
{"from": "orders", "to": "mail", "label": "notify", "style": "dotted"},
{"from": "mail", "to": "smtp", "label": "yes", "style": "thick"}
],
"groups": [{"id": "backend", "label": "Backend", "contains": ["gateway", "orders", "db"]}]
}
A Client calls an API Gateway, which routes to Orders and Orders saves to a Postgres cylinder, both inside a Backend group; Orders sends a dotted notify edge to a Send mail diamond, whose thick yes edge leads to an SMTP Relay.A Client calls an API Gateway, which routes to Orders and Orders saves to a Postgres cylinder, both inside a Backend group; Orders sends a dotted notify edge to a Send mail diamond, whose thick yes edge leads to an SMTP Relay.

Bracket shapes become shapes, -.-> a dotted edge and ==> a thick one, and a subgraph a group. An edge keeps the id Mermaid writes before its arrow (A e1@--> B), and a diff matches edges by it: give one to every edge before you start diffing versions. Without one, an edge is matched by its two ends, and a labeled edge warns edge-without-id in a diff.

Supported
Header graph or flowchart with TD, TB, LR, BT or RL
Nodes every bracket form
Edges -->, ---, -.->, -.-, ==>, === and <-->, with |label| or -- label -->; a trailing ;
Edge ids e1@ before any arrow; on an & fan-out, the edge from the last source to the first target takes it, as in Mermaid
Shorthand & fan-out and chains
Groups nested subgraph … end; a title: frontmatter
Approximated, and reported Becomes
stadium rounded
subroutine rect
doublecircle circle
trapezoid parallelogram
a fourth level of subgraphs flattened into its third-level ancestor
an edge’s properties, e1@{ animate: true } dropped: diago draws no edge animation or curve
an edge id already used kept by its first edge; the later edge has none

Rejected, with a line number: classDef, class, style, linkStyle, click, accTitle, accDescr, direction, and an edge whose endpoint is a subgraph.

sequence.mmd
sequenceDiagram
participant U as User
participant A as App
participant D as Database
U->>A: place order
A->>D: check stock
D-->>A: in stock
alt in stock
A->>D: reserve items
else out of stock
A-->>U: sorry
end
A-->>U: confirmation
sequence.json
{
"type": "sequence",
"actors": [
{"id": "U", "label": "User"},
{"id": "A", "label": "App"},
{"id": "D", "label": "Database"}
],
"interactions": [
{"from": "U", "to": "A", "label": "place order"},
{"from": "A", "to": "D", "label": "check stock"},
{"from": "D", "to": "A", "label": "in stock", "style": "dashed"},
{"from": "A", "to": "D", "label": "reserve items"},
{"from": "A", "to": "U", "label": "sorry", "style": "dashed"},
{"from": "A", "to": "U", "label": "confirmation", "style": "dashed"}
],
"fragments": [
{
"type": "alt",
"over": ["U", "A", "D"],
"sections": [
{"label": "in stock", "start": 3, "end": 3},
{"label": "out of stock", "start": 4, "end": 4}
]
}
]
}
A User places an order with an App, which checks stock in a Database; if in stock the App reserves items, otherwise it tells the User sorry, and the App then confirms to the User.A User places an order with an App, which checks stock in a Database; if in stock the App reserves items, otherwise it tells the User sorry, and the App then confirms to the User.

alt and else become one fragment with a section for each branch, over the range of messages it holds. Declared participants keep their as alias as the label.

Supported
Participants participant and actor, with as; implicit declaration on first use
Messages ->>, -->>, -) and --)
Blocks loop, opt, alt with else, par with and, and break, nested, as fragments over ranges of messages
Approximated, and reported Becomes
actor an ordinary participant
+ and - marks, activate, deactivate ignored: the engine computes the activation bars
a Note folded into the label of the nearest message before it in the block, and dropped when there is none
an empty branch or block dropped

Rejected, with a line number: critical, autonumber, create, destroy, rect, box, link, links, properties, details, and lost-message arrows (-x).

class.mmd
classDiagram
direction LR
class Animal {
<<abstract>>
+String name
+eat() void*
}
class Dog {
+bark() void
}
class Owner {
+String name
}
Animal <|-- Dog
Owner "1" --> "*" Dog : owns
class.json
{
"type": "class",
"direction": "RIGHT",
"classes": [
{
"id": "Animal",
"stereotype": "abstract",
"attributes": [{"visibility": "+", "text": "String name"}],
"methods": [{"visibility": "+", "text": "eat() void", "abstract": true}]
},
{"id": "Dog", "methods": [{"visibility": "+", "text": "bark() void"}]},
{"id": "Owner", "attributes": [{"visibility": "+", "text": "String name"}]}
],
"relations": [
{"from": "Dog", "to": "Animal", "kind": "inheritance"},
{
"from": "Owner",
"to": "Dog",
"kind": "association",
"label": "owns",
"from_card": "1",
"to_card": "*",
"directed": true
}
]
}
An abstract Animal class with a name attribute and an eat method is the supertype of Dog, which has a bark method; an Owner relates to many Dogs by an owns association.An abstract Animal class with a name attribute and an eat method is the supertype of Dog, which has a bark method; an Owner relates to many Dogs by an owns association.

Every relation operator imports with its cardinalities and label. The JSON is written with from as the subtype, implementer, owner or dependent, whichever way the Mermaid arrow pointed. A Mermaid relation has no id: import the diagram and add ids to its relations before you start diffing versions, since a diff warns edge-without-id for each relation without one.

Supported
Classes class X~T~ { … } blocks and bare declarations, with generics
Stereotypes <<stereotype>> inside a block or on its own line
Members X : +member inline, visibility prefixes, a * (abstract) or $ (static) suffix
Relations all twelve operators, with cardinalities and labels
Layout direction TB, LR, BT or RL

Rejected, with a line number: <-->, ()--, note, namespace, cssClass and click. Nothing in the class dialect is approximated.

After an import, run diago check -strict on the JSON. The importer keeps what Mermaid says, so it can bring a diagram that parses fine but reads badly, and the advisories say where.