State diagrams
stateDiagram-v2 opens a state diagram. direction TB, TD, BT, LR, or RL may appear on any statement line.
The default is top to bottom. A later direction replaces an earlier one. With a Viewport, responsive layout may
still flip a vertical author to horizontal so a wide column is used (same rules as flowcharts). Without a viewport,
the diagram keeps the direction it names.
MermoidAscent.svgDiagram(orderFsm)Transitions
From --> To declares a transition; : label names it. An id that has not been declared becomes a state, drawn as a
rounded box. The label is the id until a description replaces it.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
Idle --> Running: start
Running --> Idle: stop
""".stripMargin))That is a two-state cycle, and it lays out rather than looping forever — layering breaks cycles.
Start and end
[*] is the start or the end, depending on which side of the arrow it sits on. The start is a filled circle. The end
is a bullseye: a ring and a filled center, class state-end. Both carry start-end and neither has a label.
When a diagram uses both, mermoid paints two markers (start keeps id [*], end is [*]-end) so ranking does not
cycle through a shared node. Each composite and each concurrent region gets its own pair. A diagram that only has one
role still uses a single [*] node.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Active
Active --> [*]
""".stripMargin))Notes
note right of Idle
waiting for work
end note
right of and left of are both supported. Note text is multi-line; each line is trimmed and blank lines dropped. A
note renders as a dashed box joined to its state by a dashed connector, and the diagram's bounding box grows to hold it,
including shifting the whole diagram right when a left of note would otherwise fall outside the canvas.
When the preferred side would overlap another node (common in horizontal / flipped layouts), the placer tries the other side and then a vertical offset before settling.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Idle
Idle --> Running: start
Running --> Idle: finish
note right of Idle
no work in flight
polls every 5s
end note
note left of Running
at most one job
end note
""".stripMargin))Styling from the diagram source
classDef, class, :::, and style are the same statements as on flowcharts. classDef becomes
a CSS rule, class / ::: put the name on the state's node, and style can still set noteAlign as well as fill and
stroke.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
classDef happy fill:#1f4a35,stroke:#7dcea0
classDef warn fill:#4a4030,stroke:#e0c070
classDef sad fill:#5c2a2a,stroke:#f0a0a0
[*] --> Green
Green --> Yellow: Timer
Yellow --> Red: Timer
Red --> Green: Timer
class Green happy
class Yellow warn
class Red sad
""".stripMargin))Green:::happy --> Yellow:::warn is the same assignment written on the transition.
Note text alignment
style <state> noteAlign: left | center | right sets how that state's note text is aligned. The default is left.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Ready
Ready --> Done: go
style Ready noteAlign: center
note right of Ready
centered
note text
end note
""".stripMargin))Note aliases
Like edges, notes take as <name> to pin their element id. Without it a note is note-{stateId}-{index}, so adding an
earlier note on the same state renumbers the later ones.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Idle
Idle --> Done: go
note right of Idle as caveat
do not skip idle
end note
""".stripMargin))Self-transitions
A state can transition to itself, and stacked self-transitions stack their labels. The diagram's height accounts for the loops, and for notes pushed below them.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Retrying
Retrying --> Retrying: attempt failed
Retrying --> Retrying: backoff elapsed
Retrying --> Done: succeeded
""".stripMargin))Direction
direction LR lays the machine out left to right. TB and TD are top to bottom, BT bottom to top, RL right to
left. The line can sit above the transitions or among them.
stateDiagram-v2
direction LR
[*] --> Draft
Draft --> Live: publish
Live --> [*]
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
direction LR
[*] --> Draft
Draft --> Preparing: Launch
Preparing --> Live: Published
Live --> [*]
""".stripMargin))Back edges
A retry (Faulted --> Preparing) is a back edge. Ranking follows a depth-first search from [*] and reverses back
edges, so the fault sits one rank after the hop it returns to, not above the start. The edge is still drawn from the
fault back to the hop. A second forward parent, such as two states that both archive, stays a forward edge and the
sink lands on the last rank.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Preparing
Preparing --> Live: published
Preparing --> Faulted: failed
Faulted --> Preparing: Retry
Live --> Archived: archive
Preparing --> Archived: archive
""".stripMargin))Descriptions
The id stays the id. The words on the box are the description.
state "Waiting for a worker" as Idle
Idle : no job yet
id : text and state "text" as id are the same fact. A second, different description is a parse error that names
both strings. hide empty description paints no label on a state that was never described, and no title band on a
composite that was never described.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
state "Waiting for a worker" as Idle
Running : a job is in flight
[*] --> Idle
Idle --> Running: start
Running --> Idle: stop
""".stripMargin))Composite states
state Name { ... } draws a frame. The frame is a node in the parent diagram, and the statements inside are a diagram
of their own. A direction line inside applies only there. An edge that names the composite stops on the frame. An
edge that names a member ends on that member and crosses the frame; the member stays inside.
The same id cannot belong to two composites. The error names the id and both places. An edge between two composites, or from a member out to a sibling of the composite, is drawn. Mermaid's renderer often pulls the child out of the box. This one does not.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
direction LR
[*] --> Review
state Review {
[*] --> Screening
Screening --> Decision
}
Review --> Published: approved
Decision --> Draft: rejected
Published --> [*]
""".stripMargin))Concurrency
-- on its own line, inside a composite, splits the body into regions. Each region is ranked on its own and stacked
in the frame, with a dashed divider between them. An edge whose ends sit in two regions of the same composite is a
parse error. An edge from a state in any region out to a state outside the composite is an exit, including from a
region that is not the first.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Active
state Active {
[*] --> NumLockOff
NumLockOff --> NumLockOn : toggle
--
[*] --> CapsLockOff
CapsLockOff --> CapsLockOn : toggle
}
Active --> [*]
""".stripMargin))Choice, fork, and join
state Id <<choice>> is a diamond. <<fork>> and <<join>> are bars. A vertical flow draws a horizontal bar, and a
horizontal flow draws a vertical bar. The bar stretches to cover the centers of the states it synchronizes. [[choice]],
[[fork]], and [[join]] are the same three forms.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
state if_state <<choice>>
[*] --> IsPositive
IsPositive --> if_state
if_state --> False: if n < 0
if_state --> True: if n >= 0
state fork_state <<fork>>
True --> fork_state
fork_state --> Left
fork_state --> Right
state join_state <<join>>
Left --> join_state
Right --> join_state
join_state --> [*]
""".stripMargin))History
state H <<history>> and state H <<deepHistory>> are pseudostates, drawn as a circle labelled H or H*. [H] and
[H*] are the same two nodes written as a transition endpoint. The diagram shows the pseudostate. It does not remember
which substate was last active.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
[*] --> Active
state Active {
[*] --> Playing
Playing --> Paused
state back <<history>>
Paused --> back
}
""".stripMargin))Click and the accessible name
click is the same binding as on a flowchart: an href, a tooltip, or a callback name. accTitle and accDescr become
the SVG <title> and <desc>, and the accessible name of the hybrid root. classDef, class, :::, and style apply inside a composite, on the frame, on a
choice, on a bar, and on a marker. classDef default is the paint for a state that has no other class.
MermoidAscent.svgDiagram(Mermaid("""stateDiagram-v2
accTitle: review
classDef hot fill:#5c2a2a,stroke:#f0a0a0
[*] --> Draft
Draft --> Live: publish
class Live hot
click Live href "https://example.com" "Open"
""".stripMargin))