Mermaid vs PlantUML State Diagrams: Formal State Machines
This article compares state diagram expression only. For setup, Markdown integration, rendering, and broader tool differences, start with the Mermaid vs PlantUML overview.
The practical answer is: choose Mermaid for state diagrams that explain a feature lifecycle in everyday documentation. Choose PlantUML when the state machine itself is a design artifact with deep nesting, history, entry and exit points, or strict visual conventions.
Key Takeaways
- Both tools cover ordinary transitions, composite states, choices, and concurrent regions.
- PlantUML documents shallow and deep history plus dedicated entry and exit points.
- Use Mermaid for explanatory lifecycles and PlantUML for formal state-machine design; use the architecture comparison for system-level views.
Both tools handle the parts most teams need: start and end states, labeled transitions, composite states, choices, forks, joins, notes, direction, and concurrent regions. Mermaid is not limited to a flat row of boxes. The gap appears when the model needs UML-specific state notation such as history or entry and exit points.
This comparison follows the official syntax references:
Official documentation was checked on July 22, 2026. Mermaid's 11.16.0 state-diagram page does not document dedicated history, entry-point, or exit-point syntax, while PlantUML documents shallow and deep history plus entry and exit points. OnUML currently pins Mermaid 11.6.0; this is a verified documentation boundary, not a permanent claim about future capability.
Mermaid vs PlantUML State Diagram Differences
| Area | Mermaid | PlantUML | Recommendation |
|---|---|---|---|
| Basic states and transitions | Native | Native | Either works |
| Composite states | Supported | Supported, including deeper navigation options | Either for normal nesting |
| Choice, fork, and join | Supported | Supported | Either works |
| Concurrent regions | Supported with -- | Supported with -- or ` | |
| History states | No dedicated syntax documented on the verified Mermaid 11.16.0 page | Supports shallow [H] and deep [H*] history | PlantUML for resumable state machines |
| Entry and exit points | No dedicated syntax documented on the verified Mermaid 11.16.0 page | Supports <<entryPoint>> and <<exitPoint>> | PlantUML for UML-specific state notation |
| Styling | classDef for simple states, with composite-state limits | Themes, stereotypes, colors, and broader style control | PlantUML for diagram standards |
| Documentation friction | Short syntax and easy Markdown embedding | Requires a PlantUML renderer or integration | Mermaid for everyday docs |
The key point is not that one tool can draw states and the other cannot. It is which state-machine concepts the diagram needs to express explicitly.
A Simple Order Lifecycle Looks Similar
For a normal feature lifecycle, Mermaid and PlantUML are close. Both examples below show an order moving from draft to payment, fulfillment, and completion, with cancellation available before shipping.
Mermaid
stateDiagram-v2
[*] --> Draft
Draft --> AwaitingPayment: submit order
AwaitingPayment --> Paid: payment approved
AwaitingPayment --> Cancelled: payment expired
Paid --> Fulfilled: ship order
Paid --> Cancelled: cancel before shipping
Fulfilled --> [*]
Cancelled --> [*]
PlantUML
@startuml
[*] --> Draft
Draft --> AwaitingPayment : submit order
AwaitingPayment --> Paid : payment approved
AwaitingPayment --> Cancelled : payment expired
Paid --> Fulfilled : ship order
Paid --> Cancelled : cancel before shipping
Fulfilled --> [*]
Cancelled --> [*]
@enduml
There is no meaningful winner here. Mermaid uses stateDiagram-v2; PlantUML wraps the source in @startuml and @enduml. The transition syntax is deliberately familiar in both tools.
Use this type of state diagram for ticket lifecycles, order status, authentication status, media playback, or a small workflow. If the main goal is to explain what can happen next, Mermaid keeps the code compact.
Both Tools Support Composite States
A composite state groups internal states under one meaningful phase. That matters when Processing is not one box but a sequence of validation, reservation, and confirmation steps.
Mermaid
stateDiagram-v2
[*] --> Processing
state Processing {
[*] --> Validating
Validating --> ReservingInventory: valid
Validating --> Rejected: invalid
ReservingInventory --> Confirming: reserved
Confirming --> [*]
}
Processing --> Completed
Processing --> Failed
Completed --> [*]
Failed --> [*]
PlantUML
@startuml
[*] --> Processing
state Processing {
[*] --> Validating
Validating --> ReservingInventory : valid
Validating --> Rejected : invalid
ReservingInventory --> Confirming : reserved
Confirming --> [*]
}
Processing --> Completed
Processing --> Failed
Completed --> [*]
Failed --> [*]
@enduml
For one or two nesting levels, Mermaid remains readable. PlantUML pulls ahead when the model needs history states, transitions between internal states in different composite states, dedicated entry or exit points, or more control over how nested regions render. Mermaid's official documentation explicitly says it cannot connect internal states that belong to different composite states.
Concurrent Regions Reveal the Real Complexity
Some objects track more than one state at the same time. An active account might independently track identity verification and notification preferences. Both tools can split a composite state into concurrent regions using --.
Mermaid
stateDiagram-v2
[*] --> Active
state Active {
[*] --> Unverified
Unverified --> Verified: identity approved
--
[*] --> EmailOff
EmailOff --> EmailOn: enable email
EmailOn --> EmailOff: disable email
}
Active --> Suspended: policy violation
Suspended --> Active: review passed
PlantUML
@startuml
[*] --> Active
state Active {
[*] --> Unverified
Unverified --> Verified : identity approved
--
[*] --> EmailOff
EmailOff --> EmailOn : enable email
EmailOn --> EmailOff : disable email
}
Active --> Suspended : policy violation
Suspended --> Active : review passed
@enduml
The syntax is still close. PlantUML becomes more useful when a suspended account must resume its previous nested state. Its shallow and deep history markers, [H] and [H*], express that behavior directly. Without history semantics, a diagram often needs an explanatory note or an extra transition that is less precise.
When PlantUML Is Worth the Extra Syntax
PlantUML is the stronger choice when the diagram must document advanced state-machine notation rather than merely explain a status flow. Examples include:
- A device controller that resumes the previous operating mode.
- An embedded system with nested and concurrent regions.
- A workflow engine with explicit entry and exit points.
- A domain model reviewed using UML-specific state notation.
- A long-lived architecture document with shared themes and stereotypes.
Mermaid is usually enough when:
- The diagram belongs in a README, pull request, or product specification.
- Readers mainly need to understand valid transitions.
- Nesting is shallow and there are few concurrent regions.
- The team values code that occasional contributors can update quickly.
If the problem is actually about the order of calls between services, use a sequence diagram comparison. If it is about steps, decisions, and responsibility rather than object state, compare Mermaid and PlantUML activity diagrams.
FAQ
Can Mermaid create nested state diagrams?
Yes. Mermaid supports composite states and multiple nesting levels. For ordinary feature lifecycles, that is often enough to explain implementation behavior. PlantUML becomes more attractive when nesting also requires documented history states, entry or exit points, or other UML-specific notation.
Does Mermaid support parallel states?
Yes. A Mermaid composite state can use -- to separate concurrent regions, which is sufficient for many parallel-state explanations. PlantUML supports concurrent regions with -- or ||, giving it another explicit orientation option when the layout of a formal state-machine design document matters.
Are Mermaid and PlantUML state diagram syntaxes compatible?
They share many ideas and familiar forms, including [*], -->, composite states, and concurrency separators. They are not fully interchangeable: PlantUML history states, entry or exit points, layout choices, and styling directives do not automatically convert to Mermaid, so migration still requires semantic review.
Which is better for a production state machine?
Use Mermaid when the diagram explains the implementation. Prefer PlantUML when the document must express advanced state-machine notation such as history or dedicated entry and exit points. Neither tool validates the implementation itself, so the right choice depends on the role of the diagram, not whether the underlying software runs in production.
Bottom line:
Mermaid is the faster choice for readable state flows in documentation. PlantUML offers more explicit notation for complex or resumable state machines.
Open the OnUML editor, select the matching diagram mode, and paste each example separately to compare the rendered results.