PlantUML State Diagrams: Modeling Object Lifecycles
A state diagram captures how an object moves between states over its lifetime. PlantUML writes one as states and transitions, with support for guards, composite states, and concurrency.
A state diagram - a state machine - models the lifecycle of an object: the distinct states it can be in and the transitions that move it between them. It is the right tool whenever something has meaningful modes and rules about how it changes between them: an order that goes from created to paid to shipped, a connection that opens and closes, a document that moves through draft, review, and published. Drawing the state machine makes illegal transitions obvious, which is often where bugs hide.
PlantUML expresses state diagrams compactly, and it supports the advanced constructs that make state modelling powerful - composite states, history, and concurrent regions. This tutorial builds one up from a two-state minimum through guards, entry and exit actions, and nesting. As a text format, any machine you write can be rebuilt as an editable diagram in Atlas Diagram Studio at /diagrams. The wider PlantUML overview sits at /guides/plantuml-complete-guide, and the activity-diagram tutorial at /guides/plantuml-activity-diagram-tutorial covers modelling a process rather than a lifecycle.
States and transitions
Inside @startuml and @enduml, a transition is just an arrow between two state names: Idle --> Active draws both states, if they do not exist yet, and the arrow between them. That is genuinely all a minimal state diagram needs - PlantUML infers the states from the transitions. To declare a state explicitly, perhaps to give it a longer label or a body, use state "Awaiting Payment" as Pending, then refer to Pending in your transitions.
Two special pseudostates anchor the machine. The initial pseudostate - the filled dot showing where the object starts - is written with the built-in name, so [*] --> Idle means the object begins in Idle. The final pseudostate uses the same [*] on the target side, so Shipped --> [*] marks the end of the lifecycle. Every well-formed state diagram has exactly one initial transition and usually at least one final one, and PlantUML draws the correct filled-dot and bullseye symbols for each.
Events, guards, and actions
A bare transition is rarely enough; you want to say what triggers it and under what condition. Label a transition by appending a colon and text: Pending --> Paid : payment received names the event that causes the move. To add a guard - a condition that must hold for the transition to fire - put it in square brackets, so Pending --> Cancelled : timeout [after 24h] reads as the event timeout guarded by the elapsed-time condition. This event-guard vocabulary is what turns a diagram of boxes into a precise specification of allowed behaviour.
States themselves can carry behaviour. Inside a state's body you can specify entry and exit actions and internal activities, writing lines like Active : entry / start timer and Active : exit / stop timer, which document what happens as the object enters and leaves that state without drawing extra transitions. Internal transitions - events handled without leaving the state - are written similarly. These annotations let a single state box communicate its full contract, which is invaluable when the state machine is a specification developers will implement against.
Composite states and concurrency
As machines grow, flat lists of states become unreadable, and PlantUML's nesting and concurrency features let you manage that complexity. Use these to keep a large state machine comprehensible.
- Composite states: give a state a brace-delimited body containing its own sub-states and transitions, nesting a machine inside a state.
- Nested initial state: inside a composite state, [*] --> Substate sets where the composite begins when entered.
- History pseudostate: [H] remembers the last active sub-state so re-entering the composite resumes where it left off.
- Concurrent regions: separate parts of a composite state with a double-dash line to model orthogonal regions running in parallel.
- Choice pseudostate: the state <<choice>> stereotype draws a decision diamond for dynamic branching on guards.
- Fork and join: <<fork>> and <<join>> pseudostates split one transition into concurrent regions and merge them back.
- Notes: attach note right of a state to explain a subtle rule without cluttering the transitions.
Turning a state machine into documentation
Composite states are the key to scaling. By collapsing a cluster of related sub-states into a single named state on the top-level diagram, you let a reader grasp the high-level lifecycle first and drill into a phase only when they need the detail. Combined with the history pseudostate, which resumes a composite where it left off, PlantUML can model genuinely intricate lifecycles - the kind found in connection protocols and long-running workflows - without the diagram collapsing into a tangle.
Once the machine is captured as versioned PlantUML text, it becomes an authoritative reference that developers can implement against and that stays reviewable as the rules change. For a version that stakeholders can read and edit, or that needs presentation styling, rebuild it in Atlas Diagram Studio at /diagrams, where the same states and transitions become an editable, collaborative diagram. To draft an initial machine from a plain description of an object's lifecycle, use the AI diagram generator at /diagram-tools/ai-diagram-generator. The related sequence and activity tutorials at /guides/plantuml-sequence-diagram-tutorial and /guides/plantuml-activity-diagram-tutorial cover interactions and processes, the two dynamic views that complement this structural one.