Mermaid vs PlantUML State Diagrams: Formal State Machines

Simon Yu··Updated ·7 min read
mermaidplantumlstate-diagramstate-machinediagram-as-codeuml

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

AreaMermaidPlantUMLRecommendation
Basic states and transitionsNativeNativeEither works
Composite statesSupportedSupported, including deeper navigation optionsEither for normal nesting
Choice, fork, and joinSupportedSupportedEither works
Concurrent regionsSupported with --Supported with -- or `
History statesNo dedicated syntax documented on the verified Mermaid 11.16.0 pageSupports shallow [H] and deep [H*] historyPlantUML for resumable state machines
Entry and exit pointsNo dedicated syntax documented on the verified Mermaid 11.16.0 pageSupports <<entryPoint>> and <<exitPoint>>PlantUML for UML-specific state notation
StylingclassDef for simple states, with composite-state limitsThemes, stereotypes, colors, and broader style controlPlantUML for diagram standards
Documentation frictionShort syntax and easy Markdown embeddingRequires a PlantUML renderer or integrationMermaid 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.