PlantUML cross-tool migration — D2, Mermaid, and Draw.io workflow and pitfalls

puml.online

When a team re-picks its diagramming tool, migrating from PlantUML to D2 / Mermaid / Draw.io is common. This post distils the practical migration: which diagrams can be auto-converted, which must be rewritten, how to CI-verify the result, and when to skip migration entirely.

Why migrate?

Reasons that come up often:

  • Java team → LangChain → LLM team → D2 + Markdown is the new default
  • Data team → whole-company rendering standardised on Mermaid (GitHub-native)
  • Design collaboration → swap to a Figma-like tool (Draw.io)
  • “Avoid the JVM dependency” → migrate to Mermaid / Graphviz

Before migrating, answer: is the reason technical, or just cognitive cost? PlantUML diagrams are data — migration is data migration — detail loss is unavoidable.

Decision framework: should we?

Criteria Migrate Don’t migrate
Team size <10 people >30 people
Diagram count <50 >300
Diagram type distribution 80% flowcharts mostly UML
CI rendering dep Mutable internal plantuml server already wired
Design collaboration Design-led Engineering-led
CJK / complex chars Important Not important
Maintenance horizon <6 months Long-term (1-2y)

If “don’t migrate”:

  • Keep PlantUML → upgrade to TeaVM WASM → render locally in the browser, fully JVM-free.
  • Maintain the plantuml server → team SSR mode, zero client dependency.

If “migrate”: continue reading.

PlantUML → Mermaid

Automatic conversion is limited

Mermaid doesn’t read PlantUML — you need conversion. Reason: PlantUML and Mermaid syntax diverge widely:

Concept PlantUML Mermaid
Declaration @startuml ... @enduml ```mermaid\n...\n```
Direction arrow Alice -> Bob Alice ->> Bob
Async Alice ->> Bob Alice ->> Bob (same)
Class class User class User { }
Note note left of X Note over X
Auto-number (no native) autonumber

Hand-conversion example

PlantUML sequence:

1
2
3
4
5
6
7
8
9
10
11
12
13
@startuml
participant "用户" as U
participant "前端" as F
participant "后端" as B
database "数据库" as DB

U -> F: 点击登录
F -> B: POST /api/login
B -> DB: SELECT * FROM users WHERE email=?
DB --> B: result
B --> F: 200 token
F --> U: 跳转到首页
@enduml

Mermaid sequence (manually translated):

1
2
3
4
5
6
7
8
9
10
11
12
sequenceDiagram
participant U as 用户
participant F as 前端
participant B as 后端
participant DB as 数据库

U->>F: 点击登录
F->>B: POST /api/login
B->>DB: SELECT * FROM users WHERE email=?
DB-->>B: result
B-->>F: 200 token
F-->>U: 跳转到首页

Semi-automatic tools

  • puml2mermaid — community package, coverage ~50%
    • ✅ sequence diagrams (simple)
    • ✅ class diagram (loose)
    • ❌ state diagram
    • ❌ component diagram
    • ❌ nested package / skinparam

In practice: hand-writing 2 min/diagram vs using the tool 5 min + repeated touch-ups — hand-writing is usually faster.

PlantUML → D2

D2 itself supports plantuml import

D2 in v0.6+ supports D2 reading plantuml:

1
d2 --plantuml=path/to/diagram.puml out.svg

But only one direction: puml → D2-equivalent source, then D2 renders. Native conversion quality:

  • ✅ sequence diagram (basic)
  • ✅ class diagram (basic)
  • ⚠️ activity diagram loses if/else nesting
  • ❌ state diagram errors out directly

Converted D2 looks like

Original puml sequence:

1
2
Alice -> Bob: hi
Bob --> Alice: hi back

D2:

1
2
3
shape: sequence_diagram
Alice -> Bob: hi
Bob -> Alice: hi back

D2 declares the type with shape: sequence_diagram.

Semi-auto vs hand-write

Complex diagrams (>30 nodes, nested packages): hand-write D2 directly — the tool only helps on simple cases.

Cautions when auto-converting via D2

D2’s plantuml parser is sensitive to colons, quotes, CJK:

1
2
Alice -> Bob: 包含:冒号  # ❌ D2 parses as multiple messages
Alice -> Bob: "包含:冒号" # ✅ quoted

Any PlantUML “message contains special character” must be quoted before migration.

PlantUML → Draw.io

Draw.io uses XML; there’s no official PlantUML-to-Draw.io converter.

Manual workflow

1
2
3
4
# 1. Render the puml to SVG
plantuml -tsvg diagram.puml
# 2. In Draw.io, Import from SVG
# Draw.io → File → Import from Device → diagram.svg

Draw.io preserves the rough layout, but styles, class inheritance, notes are all lost — usually not worth it.

When to migrate to Draw.io

  • Team members need to manually tweak positions.
  • You need to mix “flowchart + rectangles + arrows” in a non-standard diagram.
  • You need shape libraries / templates.
  • You don’t want to use a code editor.

Draw.io’s core benefit is WYSIWYG — once you migrate, you can’t go back to PlantUML editing — the source is gone.

PlantUML → Graphviz DOT

Common in academic / automation scenarios. Open-source tool:

1
2
3
# https://github.com/yegor256/plantuml2dot
java -jar plantuml2dot.jar diagram.puml > diagram.dot
dot -Tsvg diagram.dot > diagram.svg

Supports:

  • ✅ component diagram
  • ✅ class diagram (simple)
  • ⚠️ sequence diagram produces ugly DOT (no sequence concept)

Per-diagram-type migration difficulty

Diagram type PlantUML → Mermaid PlantUML → D2 PlantUML → Draw.io
sequence medium (manual) low (official) high (manual SVG import)
class medium medium medium
state high (no native equivalent) high high
activity high high medium
component low (none) low (none) medium
usecase high high medium
object medium (classDiagram emulation) high medium
ER medium medium medium
gantt medium (mermaid native) medium high
mindmap medium low (both native) low

Conclusions:

  • sequence / class → Mermaid / D2 work out of the box; lowest migration cost.
  • state / activity / usecase → all tools fall short; don’t migrate, keep PlantUML.
  • mindmap → any tool works.

Step 1: inventory

1
2
3
# List all puml files
ls docs/diagrams/*.puml > inventory.txt
wc -l inventory.txt # count
1
2
3
4
5
# Detect the diagram type per file
for f in docs/diagrams/*.puml; do
type=$(grep -m1 "^@start\(uml\|sequence\|state\|class\|activity\|component\|deployment\|usecase\|object\|er\|gantt\|mindmap\|wbs\|json\|yaml\)" "$f" | sed 's/@start//')
echo "$f: $type"
done > types.txt

Step 2: group-by-type migration

1
2
3
4
5
6
# Sequence → Mermaid
while read -r file; do
echo "=== $file ==="
cat "$file"
echo "---"
done < <(grep "sequence" types.txt | cut -d: -f1)

For each diagram, hand-translate → write .mmd file → verify with mermaid CLI.

Step 3: CI consistency verification

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Render → compare structural information
plantuml -tsvg diagram.puml > /tmp/puml.svg
# Mermaid render
mmdc -i diagram.mmd -o /tmp/mm.svg

# Compare structures (node count, edge count)
puml_nodes=$(grep -c "<rect\|<circle\|<ellipse" /tmp/puml.svg)
puml_edges=$(grep -c "<path.*stroke" /tmp/puml.svg)
mm_nodes=$(grep -c "<rect\|<circle\|<ellipse" /tmp/mm.svg)
mm_edges=$(grep -c "<path.*stroke" /tmp/mm.svg)

if [ "$puml_nodes" != "$mm_nodes" ] || [ "$puml_edges" != "$mm_edges" ]; then
echo "❌ node/edge counts differ: puml($puml_nodes/$puml_edges) vs mermaid($mm_nodes/$mm_edges)"
exit 1
fi

CI passes → migration complete.

Step 4: parallel-running period

Don’t delete PlantUML files immediately after migration:

1
2
3
4
5
6
7
docs/diagrams/
├── puml/
│ ├── system.puml # old
│ └── auth.puml
└── mermaid/
├── system.mmd # new
└── auth.mmd

Three months later, confirm the mermaid version is stable in use, then delete the puml files.

When NOT to migrate

Strongly advise against migration in these scenarios:

  1. Large UML projects (>50 UML static diagrams) — PlantUML is the industry standard.
  2. CI already has a PlantUML server — no urgency.
  3. Diagrams are reverse-engineered (Java → UML) — Draw.io can’t reverse-engineer.
  4. You need !include / !function / !define PlantUML-exclusive features — other DSLs don’t have them.
  5. CJK content + large diagrams — PlantUML + Noto font combo is currently the steadiest.

Reverse migration (D2/Mermaid → PlantUML)

Sometimes “we used D2 for a year and want to switch back” or “team merge, diagrams need to be uniform”.

D2 → PlantUML

D2 official provides d2 –plantuml output for reverse-generation, but outputs fragments, not a complete .puml.

Practical approach: hand-write or write a script that scans the D2 syntax tree.

1
2
3
4
5
6
7
8
9
10
11
12
13
# scripts/d2_to_puml.py
import re, sys

def d2_seq_to_puml(d2_code):
lines = d2_code.split("\n")
puml = ["@startuml"]
for line in lines:
m = re.match(r"^(\w+)\s*->\s*(\w+)\s*:\s*(.+)$", line)
if m:
src, dst, label = m.groups()
puml.append(f'{src} -> {dst}: {label}')
puml.append("@enduml")
return "\n".join(puml)

Mermaid → PlantUML

No official tool; community scripts:

In practice, hand-writing is faster.

A set of migration helper scripts

One-shot puml → mmd trial in the repo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
# scripts/try-migrate.sh
# Usage: ./try-migrate.sh docs/diagrams/auth.puml
set -e
file=$1
name=$(basename "$file" .puml)
echo "@startuml" > /tmp/test.puml
cat "$file" >> /tmp/test.puml
echo "@enduml" >> /tmp/test.puml
plantuml -tsvg /tmp/test.puml

# Hand-write .mmd file (refer to the mermaid block in the hexo blog)
# then mmdc -i auth.mmd -o auth.svg

diff <(node svg-info.js /tmp/test.svg) <(node svg-info.js auth.svg)

In practice 80% is manual.

The decision for puml.online specifically

Our project doesn’t need migration:

  • Current primary diagram types: state, class, sequence — 5-8 each
  • CI already has hexo generator rendering PlantUML
  • CJK + Chinese markdown annotations are dense
  • Diagram count will keep growing

Migrating gives no upside — we’d migrate then migrate back.

But for your project: check the decision table above before migrating.

Recap

  • PlantUML → Mermaid: simple diagrams 1:1, state/activity come up short
  • PlantUML → D2: official reverse-import, but only basic diagrams covered
  • PlantUML → Draw.io: effectively “import SVG” + re-position, source is lost
  • 80% of migration cost is “manual rewrite” not “auto-conversion”
  • More often than not, don’t migrate — PlantUML is the steadiest UML DSL

Next

  • Title: PlantUML cross-tool migration — D2, Mermaid, and Draw.io workflow and pitfalls
  • Author: puml.online
  • Created at : 2026-07-30 12:01:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-migrate-to-d2-mermaid-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.