PlantUML vs Mermaid syntax side-by-side: 7 common diagrams, line by line

puml.online

After the macro comparison, this article is the cheat sheet for “when you actually want to draw a diagram”. Each diagram type gives you: minimal example → key differences → common pitfalls — just copy-paste to run.

Overview: which diagram types are “equally strong”

Diagram type PlantUML Mermaid Who is stronger
Sequence PlantUML (fragment / step / ref)
Class PlantUML (complete association semantics)
Flowchart Mermaid (more intuitive syntax)
ER Tie
State PlantUML (nesting / concurrency)
Gantt Mermaid (task dependencies more intuitive)
C4 architecture ✅ built-in stdlib ❌ needs plugin PlantUML

Going one by one.

1. Sequence diagram

Minimal example: Alice/Bob auth flow

PlantUML:

1
2
3
4
5
6
7
8
9
@startuml
actor User
User -> FE: Enter username & password
FE -> API: POST /login
API -> Auth: Validate token
Auth --> API: token valid
API --> FE: 200 OK + JWT
FE --> User: Redirect to home
@enduml

Mermaid:

1
2
3
4
5
6
7
8
sequenceDiagram
actor User
User->>FE: Enter username & password
FE->>API: POST /login
API->>Auth: Validate token
Auth-->>API: token valid
API-->>FE: 200 OK + JWT
FE-->>User: Redirect to home

Key differences:

Axis PlantUML Mermaid
Start markers @startuml / @enduml sequenceDiagram keyword
Solid arrow -> ->>
Dashed arrow --> -->>
Self-call A -> A: ... A->>A: ...
Async message ->> ->> no distinction; use Note right of A: async
Groups (alt/else/opt/loop) Native alt/else/opt/loop/end Native alt/else/opt/loop/end
Notes note left of User: ... Note left of User: ... (capitalized)
Participant decl actor User / participant "User Service" as US actor User / participant US as User Service
Numbering Auto Auto
Activation lifeline activate A / deactivate A activate A / deactivate A
Cross-diagram ref ref over A: ... v11+: ref over A: ...

Pitfalls:

  • Mermaid’s actor must be in the scope right after sequenceDiagram, can’t move mid-diagram
  • PlantUML note left/right/over keyword must be lowercase (note)
  • Both require else or end inside alt blocks (Mermaid strict, PlantUML tolerant)

2. Class diagram

Minimal example: User / Order

PlantUML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@startuml
class User {
-id: Long
-name: String
+login(pwd: String): Token
}

class Order {
-id: Long
-amount: Decimal
+pay(): void
}

User "1" --> "*" Order: places
User ..> Token: <<create>>

interface Payable {
+pay(): void
}
Order .|> Payable
@enduml

Mermaid:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classDiagram
class User {
-id: Long
-name: String
+login(pwd: String) Token
}
class Order {
-id: Long
-amount: Decimal
+pay() void
}
User "1" --> "*" Order : places
User ..> Token : <<create>>
class Payable {
<<interface>>
+pay() void
}
Order ..|> Payable

Key differences:

Axis PlantUML Mermaid
Visibility +/-/#/~ +/-/#/~ (same)
Static / abstract {static} / {abstract} <<static>> / <<abstract>> stereotypes
Association arrows --> (assoc) --|> (inherit) ..|> (impl) ..> (dep) Same
Multiplicity "1" --> "*" Same
Notes note left of User: ... note for User "..." (v11+)
Package / namespace package com.example { ... } namespace com.example { ... }
Generics class List~T~ class List~T~ (same)
Interface interface Payable <<interface>> stereotype
Enum enum Status { ACTIVE INACTIVE } Not native (use class to simulate)

Pitfalls:

  • Mermaid implementation uses ..|> (not ..>), easy to mistype
  • Mermaid nested class support is weak; complex hierarchies → PlantUML
  • PlantUML +login(pwd: String): Token — after the colon you cannot write a method body with spaces

3. Flowchart

Minimal example: user login decision tree

PlantUML:

1
2
3
4
5
6
7
8
9
10
11
12
@startuml
(*) --> "Enter username & password"
if "Validation passed?" then
-->[yes] "Generate JWT"
--> "Return token"
--> (*)
else
-->[no] "Return error"
--> "Log failure"
--> (*)
endif
@enduml

Mermaid:

1
2
3
4
5
6
7
8
flowchart TD
A[Enter username & password] --> B{Validation passed?}
B -->|yes| C[Generate JWT]
C --> D[Return token]
D --> End1([Done])
B -->|no| E[Return error]
E --> F[Log failure]
F --> End2([Done])

Key differences:

Axis PlantUML Mermaid
Direction top to bottom direction default flowchart TD / LR explicit
Node shapes rectangle / diamond / circle keywords [ ] { } (( )) ([ ]) symbols
Labels node1 --> "label text" node1 -->|label| node2
Subgraph rectangle cluster { ... } subgraph ... end
Styling node1 #lightblue classDef + class binding
Start/end (*) ([ ]) stadium
Comments Single line limited %% line comment

Pitfalls:

  • Mermaid node labels with special chars (/, [], ()) need quoting
  • PlantUML flowchart is weaker than sequence/class; complex flows → use Mermaid or upgrade to PlantUML activity
  • Mermaid v10+ supports same-named subgraph reuse

4. ER diagram

Minimal example: User / Order / Product

PlantUML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@startuml
entity User {
*id: Long <<PK>>
--
name: String
email: String
}
entity Order {
*id: Long <<PK>>
--
user_id: Long <<FK>>
amount: Decimal
}
entity Product {
*id: Long <<PK>>
--
name: String
price: Decimal
}
User ||--o{ Order: places
Order }o--|| Product: contains
@enduml

Mermaid:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
erDiagram
USER ||--o{ ORDER : places
ORDER }o--|| PRODUCT : contains

USER {
Long id PK
String name
String email
}
ORDER {
Long id PK
Long user_id FK
Decimal amount
}
PRODUCT {
Long id PK
String name
Decimal price
}

Key differences:

Axis PlantUML Mermaid
FK annotation <<FK>> inline Long user_id FK (after field name)
PK <<PK>> PK suffix
Cardinality ||--o{ }o--|| Same
Relationship name User --> Order: places USER ||--o{ ORDER : places
Weak entity entity Weak + relation ..|> ❌ not supported
Inheritance Parent <|-- Child ❌ not supported

Pitfalls:

  • Mermaid PK/FK keywords must immediately follow the field name (space-separated)
  • PlantUML ER is essentially an entity class diagram; can add methods; Mermaid ER only holds fields
  • Complex schema (10+ tables) → PlantUML; simple 3-5 tables → Mermaid is faster to write

5. State machine

Minimal example: order state machine

PlantUML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@startuml
[*] --> Pending
Pending --> Paid: pay
Paid --> Shipped: ship
Shipped --> Delivered: confirm
Paid --> Refunded: refund
Delivered --> [*]
Refunded --> [*]

state Paid {
[*] --> AwaitingShipping
AwaitingShipping --> Shipping
}
@enduml

Mermaid:

1
2
3
4
5
6
7
8
9
10
11
12
13
stateDiagram-v2
[*] --> Pending
Pending --> Paid : pay
Paid --> Shipped : ship
Shipped --> Delivered : confirm
Paid --> Refunded : refund
Delivered --> [*]
Refunded --> [*]

state Paid {
[*] --> AwaitingShipping
AwaitingShipping --> Shipping
}

Key differences:

Axis PlantUML Mermaid
Start marker [*] [*] (same)
Nesting state Outer { state Inner { ... } } state Outer { ... }
Concurrency state A { -- || ==} Not supported
Choice state c1 <<choice>> state c1 <<choice>> (same)
History state state X <<history>> ❌ not supported
Notes note right of State: ... note right of State : ...
Entry/exit actions State : entry / action Not supported

Pitfalls:

  • Mermaid must use stateDiagram-v2 (v1 deprecated)
  • PlantUML deep nesting gets messy; wrap with package
  • Mermaid nested state indentation must be strict (4 or 2 spaces consistently, no mixing)

6. Gantt

Minimal example: two-week sprint plan

PlantUML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@startuml
title Sprint 23
dateformat YYYY-MM-DD
scale 1920 width

[Requirements review] as [req] lasts 1 days
[Design] as [design] starts at [req]'s end
[Dev] as [dev] starts at [design]'s end
[dev] lasts 5 days
[Integration] as [int] starts at [dev]'s end
[int] lasts 2 days
[Release] as [rel] starts at [int]'s end
[rel] lasts 1 days
@enduml

Mermaid:

1
2
3
4
5
6
7
8
9
10
11
gantt
title Sprint 23
dateFormat YYYY-MM-DD
section Prepare
Requirements review :a1, 2026-08-04, 1d
Design :a2, after a1, 2d
section Develop
Dev :a3, after a2, 5d
section Verify
Integration :a4, after a3, 2d
Release :a6, after a5, 1d

Key differences:

Axis PlantUML Mermaid
Task dependency starts at [task]'s end after a1 (using alias)
Milestone today is milestone :milestone, m1, 2026-08-10, 0d
Progress [task] lasts 5 days and is 60% completed :a3, after a2, 5d + done status
Status done active crit done active crit
Grouping Implicit (by order) section explicit
Date format dateformat YYYY-MM-DD dateFormat YYYY-MM-DD
Workdays monday are closed Not supported

Pitfalls:

  • PlantUML alias must be [a] as [b] declared upfront to reference later
  • Mermaid milestone uses 0d
  • PlantUML lacks section explicit grouping; long Gantts get messy

7. C4 architecture

Minimal example: System Context

PlantUML (using C4-PlantUML stdlib):

1
2
3
4
5
6
7
8
9
10
@startuml
!include <C4/C4_Context>

Person(user, "User", "Uses the system")
System(webapp, "Web App", "Provides UI")
System_Ext(payment, "Payment Service", "External payment")

Rel(user, webapp, "Uses")
Rel(webapp, payment, "Calls payment API", "HTTPS")
@enduml

Mermaid (v11+ experimental):

1
2
3
4
5
6
7
8
%%{init: {"theme": "default"}}%%
C4Context
title System Context
Person(user, "User", "Uses the system")
System(webapp, "Web App", "Provides UI")
System_Ext(payment, "Payment Service", "External payment")
Rel(user, webapp, "Uses")
Rel(webapp, payment, "Calls payment API", "HTTPS")

Key differences:

Axis PlantUML Mermaid
C4 support Official stdlib (25+ built-in diagrams) v11+ experimental, needs %%{init}%%
Container / Component / Dynamic All present Also present (v11+)
Themes LAYOUT_WITH_LEGEND() dozens Default 1
Deployment Deployment_Node C4Deployment
Risk None (mature) v11+ API changes, docs lacking

Conclusion: For C4, PlantUML wins handily. Mermaid only supports C4 from v11+, syntax is unstable, docs lag.

Quick reference: 5 most common pitfalls

  1. Mermaid notes: note left of A: ... (capitalized)
  2. PlantUML notes: note left of A: ... (lowercase)
  3. Arrow direction: Mermaid solid ->> dashed -->> (double >); PlantUML solid -> dashed -->
  4. Class realize: Both use ..|> (not ..>)
  5. State diagram start: Mermaid must use stateDiagram-v2, not stateDiagram

Cross-tool migration tips

  • PlantUML → Mermaid: Class and sequence migrate most cleanly; ER and state lose some attributes
  • Mermaid → PlantUML: PlantUML syntax is more permissive; almost always works; watch out for flowchart → PlantUML activity losing some node shapes
  • Auto-convert tools: mermaid-to-plantuml (Node CLI) and plantuml-to-mermaid (Python, bigger loss)

How to decide: which to draw with?

Back to the decision:

  • Simple flowcharts / README / doc embedding → Mermaid (copy-paste ready)
  • Strict UML / complex class / C4 → PlantUML
  • GitHub README → Mermaid (native ```mermaid)
  • CI batch validation → PlantUML (CLI is rigorous)
  • Cross-language (Chinese/Japanese/emoji) → Mermaid first (fewer font issues)

Further reading

  • Title: PlantUML vs Mermaid syntax side-by-side: 7 common diagrams, line by line
  • Author: puml.online
  • Created at : 2026-08-04 09:30:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-mermaid-syntax-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.