Skip to content
Connect an agent
Other diagram kinds

State diagrams

A state diagram shows the states of something and the events that move it from one to another: an order, a ticket, an agent run, a connection, a download, a release. Use it for a lifecycle or a state machine. It is for anyone, human or AI agent, who needs the answer to "what can happen next from here, and why".

TL;DR

QuestionAnswer
What is it for?A lifecycle or a state machine: states, the transitions between them, and the events, conditions and actions on each transition.
How does it start?state "Title", then state id "Label" for a state and a -> b : "event [guard] / action" for a transition. The words start and end in a transition draw the start and end markers.
How do I make it readable?One line per state: state paid "Paid" sub="Money received" tone=green icon=lucide:credit-card. Green is for success, amber for waiting, red for failure, and blue or violet for work in progress. See Make it readable.
What else can it hold?Composite states with states inside, regions that run side by side, a choice, a fork and a join, a history marker, and notes.
Can I convert Mermaid?Yes. stateDiagram and stateDiagram-v2 become a state diagram. They used to become a flow diagram.
How big may it be?300 nodes, 600 transitions and 6 levels of nesting. Past a limit the diagram is refused with error E140, before anything is drawn.
How is it drawn?A rounded card for each state with an icon and a tint, a frame with a corner tab for a composite state, a dot and a ring for start and end, and a pill with a branch icon for a choice. Layout is automatic, and the theme chooses every color.

Ask your agent

Say what the states are and what moves the thing from one to the next, then ask for an icon on every state and a color for what each state means. The agent writes the rest.

text
Use Plotwork to draw the lifecycle of an online order as a state diagram. An order starts as Pending, waiting for payment. A valid card payment moves it to a stock check, and nothing within 24 hours cancels it. If the item is in stock, it is picked and packed in a Fulfilment state and then delivered. If it is sold out, the order is cancelled and the money refunded. Give every state an icon, color the states by what they mean, and add a note that refunds stay open for 30 days.
text
Use Plotwork to draw the run of an AI agent as a state diagram. The agent plans a step, then runs it with a tool, or waits for a person to approve it when the step is risky. A tool error is retried after a backoff while retries are left, and the run fails when they run out or when a person denies a step. The run is Done when the goal is met. Put an icon on every state, and use green for success, amber for waiting, and red for failure.
text
Use Plotwork to draw the lifecycle of a subscription as a state diagram. A customer starts a 14 day trial, and is charged when it ends if a card is on file, or cancelled if not. An active subscription renews each period. A failed payment makes it Past due and is retried on days 3, 5, and 7, and after 21 days the subscription is cancelled and access is revoked. Give each state an icon and a tone that shows how healthy it is.

Example

This is the order lifecycle from the first prompt. It shows a composite state with an icon of its own (Fulfilment, with its own start and end), a choice, transition labels that carry an event, a guard and an action, and a note.

Order lifecycle: 6 states (1 composite), 11 transitions. An order is paid, checked against stock, packed and delivered, or cancelled.

Make it readable

A state diagram reads fastest when the picture says how things stand before anyone reads a word. Four habits do that, and an agent can write all of them in one line per state, as the order lifecycle above does.

HabitHowExample
Color shows how it is goingtone=green for success, tone=amber for waiting or needing attention, tone=red for failure, tone=blue or tone=violet for work in progress, and no tone for a state at rest.state failed "Failed" tone=red
One icon on every stateicon= names what the state is, so a card is clear at a glance. A status: icon has a tone of its own, such as status:success in green and status:error in red, and a simple: icon is the mark of a tool.icon=lucide:credit-card, icon=status:error, icon=simple:docker
One fact in sub=The one detail that decides what happens next, such as a limit, a duration, or who acts.sub="Waiting for payment"
The guard and the action in the labelThe event says what happened, the guard in brackets says when the transition may run, and the action after a slash says what the system does."pay [card valid] / charge card"

Keep a state name to a few words and move detail into sub= or a note: text wider than a card is cut with warning W203. Do not rely on color alone. The name and the icon say the same thing, so the diagram still reads for someone who cannot tell two tones apart. An icon name must exist, so ask search_icons or map_icons for it, and never invent one.

An AI agent run

An agent run shows tones that say how each step is going, a loop back to planning, and guards that choose between two transitions out of one state.

Agent run: 6 states, 12 transitions. An agent plans, runs tools, waits for approval on risky steps, retries on errors, and finishes or gives up.

A subscription lifecycle

A subscription shows a transition from a state to itself (the renewal), several ways into one state (Cancelled), and labels that carry an event, a guard and an action.

Subscription lifecycle: 4 states, 9 transitions. A customer starts a trial, pays every period, may fall behind on payment, and cancels or lapses.

A release pipeline

A release pipeline shows a fork and a join around checks that run in parallel, a choice on the result, and the brand icons of the tools from the simple set.

Release pipeline: 7 states, 14 transitions. A push runs three checks in parallel, joins them, and ships only when all are green.

A connection that remembers its state

A live connection shows a composite state with a history marker, so a reconnect returns to Idle or Streaming, whichever was active when the network dropped.

Live connection: 6 states (1 composite), 10 transitions. A connection opens, drops, and comes back to the state it was in.

A download with two regions

A download shows regions, two parts of one state that run side by side, and a transition from a state to itself.

Download manager: 8 states (1 composite), 13 transitions. A download runs its transfer and its progress display side by side, each as a region of one state.

The language

A line is one statement. A # starts a comment. Ids are words such as pending, in-stock or _x, labels are in double quotes, and options are written key=value. The first line is the header.

The header

Plotwork
state "Order lifecycle" direction=right desc="How an order moves from payment to delivery."
PartMeaning
stateThe kind.
"Title"The title. It is the accessible name of the picture and heads the text description. Optional.
direction=auto (the default), right, down, left, or up. auto draws the diagram left to right and, when that is wider than a Markdown column, also top to bottom, and keeps the narrower one.
desc=A plain-language description for the text description. Optional. A desc "text" statement does the same.

Statements

StatementMeaningExample
state <id> "Label" sub="Detail" tone=<tone> icon=<set:name>A state, drawn as a rounded card. sub= is the description under the name, and a \n in it starts a new line. A tone other than neutral tints the card. icon= puts an icon before the name.state paid "Paid" sub="Money received" tone=green icon=lucide:credit-card
state <id> "Label" icon=<set:name> {, the states inside, then }A composite state: a state that holds other states, drawn as a frame with its name, and its icon if it has one, in a corner tab. sub= goes in the tab as one line. The } is on a line of its own.state shipping "Shipping" icon=lucide:package {
<from> -> <to> : "event [guard] / action"A transition. Every part of the label is optional. Only -> draws a transition.pending -> paid : "payment received [card valid] / charge card"
start -> <id>The start marker of the scope the line is in: a filled dot.start -> pending
<id> -> endThe end marker of the scope the line is in: a ring with a dot.delivered -> end
choice <id> "Question?" tone=<tone> icon=<set:name>A pill with a branch icon where one path splits on conditions. The question is optional: with none it is a small round chip holding only the icon. The default tone is amber, icon= swaps the icon, and icon=none drops the chip.choice stock "In stock?"
fork <id>A bar where one path splits into parallel paths.fork split
join <id>A bar where parallel paths merge.join merge
history <id> deep=onA circle holding H, or H* with deep=on, inside a composite state. A transition into it returns to the state that was active last.history last deep=on
region "Name" {, the states inside, then }One of the parts of a composite state that run side by side. The name is optional.region "Reader" {
note <id> : "text" side=<side>A note on a state, a choice, a bar, or a composite state. side= is left, right, top, bottom, or left out.note paid : "Refunds stay open for 30 days" side=right
desc "text"The description of the diagram.desc "How an order moves."

tone= takes neutral, blue, violet, aqua, orange, green, amber, or red, and icon= takes a name from the closed list of icons, such as lucide:play, status:success or simple:github. A diagram cannot set a color: the theme chooses every color, in light and dark. Make it readable says which tone and icon to choose.

The transition label

The label of a transition reads event [guard] / action, as in UML. The parser splits it, so the picture sets the event in the primary ink and the guard and the action in the secondary ink, and the text description says "on the event, if the guard, then the action".

WrittenEventGuardAction
payment receivedpayment receivednonenone
[in stock]nonein stocknone
pay [card valid] / charge cardpaycard validcharge card
close / send FINclosenonesend FIN
/ notify supportnonenonenotify support
GET /ordersGET /ordersnonenone
array[0] accessarray[0] accessnonenone

A slash starts the action only when a space follows it, and brackets are a guard only when they are the last thing before the action or the end of the label. So GET /orders and array[0] access are events. Text wider than 300 px is cut with an ellipsis and warning W203; the full text stays in the text description.

Start, end, and scopes

A scope is the top level of the diagram, a composite state, or one region.

RuleDetail
start and end are words, not idsstate start "Start" is error E360. A transition into start or out of end is error E361.
A scope has one start and one endThe marker is made the first time a transition in that scope names it, however many transitions name it.
The marker belongs to where the line is writtenstart -> a inside state shipping "Shipping" { is the start of Shipping.
A transition may cross scopesIds are global, so a line inside one composite state may name a state in another.
A transition may enter or leave a composite state by its idorder -> shipping enters at the start marker of Shipping, and shipping -> delivered leaves from its frame. A transition between a composite state and a state inside it is error E365.
A state may loop to itselfA composite state may too. A choice, a bar, a marker, or a history marker may not (E364).

Regions

A region goes directly inside a composite state, and a composite state holds either states or regions, never both (E362). A region outside a composite state, or inside another region, is E363. Each region has its own start and end markers. A diagram that reads sideways stacks its regions, a diagram that reads up or down puts them in a row, and a dashed line crosses the whole frame between two of them. A download with two regions draws this.

Plotwork
state sync "Syncing" {  region "Upload" {    start -> sending    state sending "Sending" icon=lucide:cloud-upload  }  region "Download" {    start -> receiving    state receiving "Receiving" icon=lucide:cloud-download  }}

Forks, joins, and choices

A fork splits one path into parallel paths and a join brings them together, so use them when several things must all happen. A choice splits one path on conditions, so give every branch out of it a guard. A release pipeline uses both.

Plotwork
fork splitjoin mergechoice green "All green?"
split -> lintsplit -> testlint -> mergetest -> mergemerge -> greengreen -> deploy : "[yes]"green -> fix : "[no]"

History

A history marker sits inside a composite state. A transition into it returns to the state that was active last, and deep=on makes it remember the state of nested composite states too. It is an error outside a composite state (E366). A connection that remembers its state draws this.

Plotwork
state player "Player" {  history last deep=on  start -> stopped  state stopped "Stopped"  state playing "Playing" icon=lucide:play  stopped -> playing : "play"  playing -> stopped : "stop"}state off "Off"
start -> playerplayer -> off : "power"off -> last : "power"

Notes

A note sits across the reading direction, so a transition, which runs along it, never meets one. side= is a request: a side along the reading direction moves to the nearest side across it.

The diagram readsleftrighttopbottomNo side
Right or leftabovebelowabovebelowbelow
Down or upleftrightleftrightright

Notes on one side stack in the order they are written, nearest first, and a dotted line joins each note to its state. A note on a composite state has no free side, so it sits inside the frame, under what the frame holds, with no connector. A note on a start or end marker is error E367.

Limits and warnings

Warnings never stop a diagram from being drawn. Errors do.

LimitValueWhat happens past it
Nodes of any kind300E140
Transitions600E140
Layers skipped by transitions400E140
Nesting of composite states6E140
Regions in one composite state8E140
Notes100E140
States before the density warning15, plus 1 for each composite state, up to 25W200
Nesting before the warning3W334

Layers skipped counts how far transitions jump over the rest of the diagram: a transition that passes over many layers makes the layout work much harder than the number of states suggests, so that number is limited, not only the number of states. The limits are checked from the text alone, so a diagram that is too big is refused at once with a hint on how to split it.

CodeSeverityWhenFix
E360errorstart or end is used as an id.Pick another id, and write the marker as a transition.
E361errorA transition goes into start or out of end.The marker only begins or finishes a transition.
E362errorA composite state holds both states and regions.Put the state into a region.
E363errorA region is outside a composite state, or inside a region.Open it inside a composite state.
E364errorA choice, a bar, a marker, or a history marker loops to itself.Loop through a state.
E365errorA transition joins a composite state and a state inside it.Start the transition from inside.
E366errorA history is outside a composite state.Put it inside the composite state it belongs to.
E367errorA note is on a start or end marker.Attach it to the state next to the marker.
E140errorThe diagram is over a limit in the table above.Split the diagram.
E100, E101, E110, E111, E121 to E123, E130, E131, E133 to E136errorThe shared codes: a syntax error, an unterminated string, an unknown statement or header, an unknown tone, icon, or direction, a duplicate id, an unknown state, an unclosed or unmatched block, an unknown option, or a bad option value.The message names the line and says what to write. For an unknown state it gives the nearest id.
W200warningThere are more states than the density limit.Group states into composite states, or split the diagram.
W201warningA state has no transition at all.Connect it or remove it.
W202warningThe diagram is wider than a column would keep readable.Use direction=down, shorten the labels, or split it.
W203warningText was cut with an ellipsis.Shorten the text, or move the detail into sub= or a note.
W330warningNo path from the top-level start reaches a state.Add a transition into it.
W331warningA state has no way out, in a part of the diagram that has an end.Add a transition out, such as name -> end.
W332warningA choice has fewer than two ways out, or a branch has no condition.Give every branch a guard.
W333warningA fork has fewer than two transitions out, or a join has fewer than two in.Give it two, or use a state.
W334warningComposite states are nested more than 3 deep.Flatten them, or draw the inner machine as its own diagram.

W330 is checked only when the top level has a start marker, and a composite state with no start of its own begins wherever nothing leads into it. W331 looks at one scope at a time: a state is a dead end only when its own scope has an end marker and neither it nor a composite state around it has a transition out.

From Mermaid

stateDiagram and stateDiagram-v2 convert into a state diagram. Before the state kind existed they were converted into a flow diagram, with the start and end as nodes and each composite state as a group. The converter now writes a state diagram, checks it, and returns nothing for one that would not validate. Call convert_mermaid on one diagram, or sync_markdown with convertMermaid for a whole file, as the migration guide describes. The conversion adds no icons and keeps no colors, apart from a tone taken from a class name such as error, so choose a tone and an icon for each state afterwards, as Make it readable describes.

Mermaid
---title: Print job---stateDiagram-v2  [*] --> Queued  Queued --> Printing : printer free  Printing --> Done  Printing --> Failed : jam  Failed --> Queued : retry  Done --> [*]  state Printing {    [*] --> Warming    Warming --> Spooling    Spooling --> [*]  }  note right of Failed : Paper jams are the usual cause  classDef bad fill:#f96  class Failed bad

The converter writes this, and reports two warnings M200 for the dropped classDef and class lines:

Plotwork
state "Print job"
state Queued "Queued"state Printing "Printing" {  state Warming "Warming"  state Spooling "Spooling"
  start -> Warming  Warming -> Spooling  Spooling -> end}state Done "Done"state Failed "Failed"
start -> QueuedQueued -> Printing : "printer free"Printing -> DonePrinting -> Failed : "jam"Failed -> Queued : "retry"Done -> end
note Failed : "Paper jams are the usual cause" side=right

What is kept

MermaidPlotworkNote
A --> B : eventa -> b : "event"The label is kept as written. A chain such as A --> B --> C : x is two transitions, with the label on the last.
state A, state "Long name" as Astate a "Long name"The id stays. A state first mentioned in a transition is declared when the diagram is written.
A : descriptionsub="line one\nline two"Each description line stays its own line.
[*] --> A, A --> [*]start -> a, a -> endThe marker of the scope the line is in. Inside a composite state it is that state's own.
state A { and its statesstate a "A" { and its statesNesting is kept up to 24 levels (M102 past that). A transition into or out of the composite state is kept as written.
-- inside a composite stateregion blocksWhat comes before the first -- is the first region. One region alone is no split.
<<choice>>, [[choice]]choice id
<<fork>>, <<join>>fork id, join idThey are bars now, so there is no warning.
note right of A : text, or a note with end notenote a : "text" side=rightA multi-line note joins its lines with a space. A note on a state that was never mentioned declares it.
direction LRdirection=right in the headerTB and TD give down, BT up, and RL left.
accTitle, accDescr, a front matter title:The title and desc=A front matter title wins.
%% comment, on a line of its own or at the end of oneIgnoredA %% inside quotes is text.

What is dropped or changed

MermaidWhat happensCode
classDef, class, :::name, style, %%{init}%%, front matter config:Dropped. A class name that means a tone (error, danger and fail give red, warning gives amber, and success, ok, done and pass give green) gives the state that tone=, and the class itself is dropped.M200, and M207 for the tone
clickDropped. A diagram is a static picture with no links inside it.M201
scale, hide empty description, and a direction inside a composite stateDropped. Plotwork chooses the scale and has one reading direction for the whole diagram.M202
A -- outside a composite state, a transition that loops a choice or a bar to itself, and a transition between a composite state and a state inside itDropped, because the meaning has no Plotwork form.M203
A stereotype Plotwork has no shape for, such as <<expandable>>Drawn as a plain state.M204
HTML tags, Markdown marks, line breaks and entities such as &quot; in a labelDecoded or removed.M205
An id that is not a valid Plotwork idRenamed. The label keeps the original name.M206
An exact repeat of a transitionDropped.M209
An empty composite state, an empty region, or an empty descriptionThe composite state becomes a plain state, and the empty region is dropped.M216
A composite state or a note that is never closed, or a } that closes nothingRepaired.M217
A line that cannot be readAn error, or with lenient a warning that skips the line.M110, M218
A note on a composite stateDrawn inside its frame, under the states it holds, because the frame has no free side.M250
A state named start or endRenamed (start_state), because the markers use those words.M251

The old conversion differed in ways that matter if you kept its output. A state was node id step "Label" and is now state id "Label", with no node types. The start and end were terminal nodes with ids such as start_Active and are now markers written as transitions. A note and a -- were dropped and are now kept, as a note and as regions. A transition into a composite state was redrawn to its start node, and is now kept as written. <<fork>> and <<join>> were plain steps and are now bars.

See also

DocWhy
Writing diagramsThe flow, sequence, and schema kinds, and the options every kind shares
Examples to copyFinished diagrams with the prompt that gets an agent to write each one
Migrating from MermaidThe converter, its diagnostic codes, and what carries over
Icon referenceEvery icon name you can put in icon=
Git graphsA repository history drawn as lanes
Gantt chartsA plan drawn on a calendar
Class diagramsClasses, their members, and how they relate
TimelinesEvents in time order
User journeysThe steps of a person, scored by how they feel
Mind mapsAn idea and its branches
Language referenceEvery word of the language, one page for each topic
Diagram kindsThe kinds, and what each one draws