Mermaid vs PlantUML vs Graphviz: Diagrams as Code Compared

Mermaid vs PlantUML vs Graphviz: Diagrams as Code Compared

A README with no diagram is fine for a small library, but the moment you have to explain a four-service auth flow or a state machine that rejects half its inputs, you reach for a picture. The question is which picture — and more importantly, which source code for the picture, because hand-drawn boxes in Figma die the day someone changes a service name.

Three tools dominate the diagrams-as-code world: Mermaid, PlantUML, and Graphviz. They overlap enough that engineers pick one almost at random and stick with it. They differ enough that picking the wrong one means fighting your tool every time the diagram grows. This post compares them on syntax, layout engine, output, and the boring practical bits — what renders inline on GitHub, what scales to a 50-node architecture map, what you can paste into a Confluence page without writing a plugin.

flowchart LR
  SRC[Diagram source<br/>.mmd, .puml, .dot] --> PARSE[Parser<br/>tokens + AST]
  PARSE --> LAY[Layout engine<br/>Dagre / Graphviz / custom]
  LAY --> RENDER[Renderer<br/>position glyphs + edges]
  RENDER --> OUT[SVG / PNG / PDF]
  OUT --> HOST[GitHub README<br/>Confluence<br/>static asset]

The Same Diagram, Three Times

Before any opinions, here is the same simple flow — a request hitting an API gateway, getting authenticated, then either succeeding or returning 429 — written in all three tools.

Mermaid:

flowchart LR
  A[Client] --> B{API Gateway}
  B -->|valid token| C[Auth Service]
  B -->|invalid| E[401]
  C -->|allowed| D[Backend]
  C -->|rate limited| F[429]

PlantUML:

@startuml
skinparam monochrome true
[Client] --> [API Gateway]
[API Gateway] --> [Auth Service] : valid token
[API Gateway] --> [401] : invalid
[Auth Service] --> [Backend] : allowed
[Auth Service] --> [429] : rate limited
@enduml

Graphviz (DOT):

digraph G {
  rankdir=LR;
  Client -> Gateway;
  Gateway -> Auth [label="valid"];
  Gateway -> "401" [label="invalid"];
  Auth -> Backend [label="allowed"];
  Auth -> "429" [label="rate limited"];
}

Mermaid reads almost like prose. PlantUML uses bracketed components — recognisable to anyone who has touched UML before. Graphviz is a graph description language, terse and a little austere. Same diagram, three philosophies. The differences only get sharper as diagrams get bigger.

Mermaid vs PlantUML vs Graphviz: where each one wins Pick by where the diagram has to render Mermaid JS, Dagre layout GitHub native yes UML semantics basic 50+ node layout poor Runtime needed none Best for READMEs, dev docs, small flowcharts PlantUML Java + Graphviz GitHub native no UML semantics rich 50+ node layout ok Runtime needed JVM Best for Confluence, formal UML, sequence diagrams Graphviz DOT, multiple algos GitHub native no UML semantics no 50+ node layout great Runtime needed binary Best for dependency graphs, large state machines

Mermaid: The Markdown-Native Choice

Mermaid is the youngest of the three and the most aggressive at fitting into modern workflows. It runs entirely in JavaScript, which means it renders in the browser — no server, no Java runtime, no installed graphviz binary. That single fact is why GitHub natively renders Mermaid blocks inside Markdown files since 2022. GitLab, Notion, and Obsidian followed.

That integration changes how you treat diagrams. Instead of rendering a PNG and committing it next to your README, you write a fenced code block with mermaid as the language and the platform draws it for you. The diagram lives inside the document. When the architecture changes, you edit text — same as the rest of the README.

Mermaid covers more than flowcharts. It does sequence diagrams, state machines, ER diagrams, Gantt charts, class diagrams, user journeys, and a few experimental types. The Mermaid documentation is the canonical reference and is genuinely good.

The trade-off is layout. Mermaid uses Dagre under the hood, which works fine for small diagrams but produces awkward results once you have more than 20 nodes — long edges crossing each other, nodes drifting too far apart, labels overlapping arrows. You can nudge it with subgraphs and explicit ordering, but you can never really control it the way Graphviz lets you. For a six-step flowchart it is perfect. For a 40-service system diagram, it falls apart.

If you mostly write developer documentation that lives in Markdown — READMEs, GitHub wikis, design docs, blog posts — Mermaid is almost always the right choice. Try it in the browser at Mermaid Live Editor before reaching for anything heavier.

PlantUML: The UML Heavyweight

PlantUML predates Mermaid by about a decade and grew up in the Java enterprise world. That heritage shows: it speaks UML fluently — sequence diagrams, class diagrams, component diagrams, deployment diagrams, use case diagrams, activity diagrams, state diagrams, timing diagrams, and several types you will never need unless you work somewhere with a 200-page architecture review document.

The syntax leans on familiar UML conventions. Classes have stereotypes, components have ports, sequences have lifelines and activations. If you trained as a software architect or worked anywhere that writes RFCs in Confluence, this feels like home. If you didn't, it feels like learning a second language to draw a box.

PlantUML's strength is sequence diagrams. They are noticeably better than Mermaid's — cleaner spacing, richer semantics (notes, dividers, groups, references), and faithful UML output that holds up in formal documents. For protocols and message flows where the exact ordering and the exact actor matter, PlantUML wins.

The downsides are real:

  • It needs a Java runtime, or a server (often a Docker container running the official PlantUML server). You don't get GitHub-native rendering.
  • The syntax is more verbose than Mermaid for simple cases.
  • Layout uses Graphviz under the hood for some diagram types, so you inherit some of Graphviz's quirks without all of its control.

PlantUML pays off in serious documentation systems — Confluence, Sphinx, MkDocs, AsciiDoc — where you can run a PlantUML server, the diagrams are formal, and consistency across hundreds of pages matters more than someone glancing at a README on GitHub. The PlantUML site covers every diagram type with examples. Try the syntax inline in PlantUML Renderer.

Graphviz: The Layout Engine Underneath Everything

Graphviz is the oldest of the three and the most powerful, in the same way awk is more powerful than a shell loop — it does one thing precisely and gives you control nothing else does. It is a graph layout engine. You describe nodes and edges in DOT, and Graphviz runs an algorithm — dot, neato, fdp, sfdp, circo, twopi — to position them. Each algorithm is tuned for a different shape of graph: hierarchical, force-directed, radial, circular.

This control is why Graphviz is the layout engine behind a lot of other tools. PlantUML uses it. Doxygen uses it. Many academic graph visualisers use it. The Graphviz documentation is dense and worth reading once.

What Graphviz gives you that the others don't:

  • Real layout algorithms, not just one. A 200-node force-directed layout is something neato does well and Mermaid will never do.
  • Fine-grained styling — fonts, colours, node shapes, edge styles, ranks, clusters, ports.
  • Predictable, deterministic output. Same DOT in, same SVG out, every time.
  • Output formats: SVG, PNG, PDF, PostScript, JSON, plain text.

What it gives up:

  • No native UML semantics. You are drawing graphs, not class diagrams.
  • The syntax is the most foreign of the three for newcomers.
  • No platform renders DOT inline (no GitHub-native support).

Graphviz is the right answer when you have a graph — a dependency tree, a call graph, a network topology, a finite state machine with dozens of transitions. For systems that look like graphs rather than narratives, nothing else comes close. Pipe a DOT file into Graphviz Renderer to see the difference between dot and neato on the same graph.

How to Choose

The three tools optimise for three different situations, and once you see the split, the choice gets easy.

Use Mermaid when the diagram lives next to code in a Markdown file. READMEs, GitHub wikis, design docs, blog posts, internal handbooks. The Markdown-native rendering is decisive — your diagram is part of the document, not an attachment to it. Small to medium flowcharts, sequence diagrams, and ER diagrams render beautifully. Don't push it past about 30 nodes.

Use PlantUML when you are writing formal architecture documentation, especially anything UML-shaped. Sequence diagrams of protocol exchanges. Class diagrams for a Java/C# codebase. Component diagrams for a system that needs an enterprise-style architecture record. Confluence and Sphinx integrations are mature. Expect to run a server.

Use Graphviz when the thing you are drawing is genuinely a graph and the layout matters. Service dependency maps, call graphs, finite state machines, network topologies, anything generated programmatically from data. If you find yourself fighting Mermaid's layout engine, that is the signal to switch to Graphviz.

A reasonable team can use all three. A startup engineering org might pick Mermaid for READMEs (because GitHub renders it), Graphviz for the auto-generated service dependency map (because the dependency tool emits DOT), and PlantUML for the formal API protocol diagrams in the architecture wiki (because the protocol team is UML-fluent).

flowchart TB
  Q{Where will this<br/>render?} --> M[GitHub README,<br/>Notion, GitLab MR]
  Q --> P[Confluence,<br/>Sphinx, AsciiDoc]
  Q --> G[CI artifact,<br/>auto-generated]
  M --> SHAPE1{Diagram size?}
  SHAPE1 -- "<30 nodes" --> USE1[Use Mermaid]
  SHAPE1 -- "30+ nodes" --> USE3[Use Graphviz<br/>render to SVG]
  P --> KIND{UML semantics?}
  KIND -- "yes" --> USE2[Use PlantUML]
  KIND -- "no" --> USE3
  G --> USE3

Picking the Right Tool for the Doc, Not the Diagram

A useful reframe: the question is rarely "what is the best diagram tool" and almost always "what is the best diagram tool for this document". GitHub README? Mermaid. Confluence architecture page? PlantUML. Auto-generated dependency graph in your CI pipeline? Graphviz. A Markdown blog post you'll convert to PDF for stakeholders? Mermaid first, and use Markdown to PDF once the content is settled.

Match the tool to where the diagram has to render, not to which syntax you find prettiest. The wrong tool is the one that requires a build step, a plugin, or a screenshot to show up where readers actually read.

When You Don't Need Any of Them

If you are diagramming once for a slide deck and won't change it again, none of these tools are the right answer. Open a drawing app, drag boxes, export PNG. Diagrams-as-code wins when the diagram is going to be edited — when the architecture changes, when a service gets renamed, when a state gets added. If the diagram is a one-off, the round trip through a syntax is wasted effort.

The same is true of trivial diagrams. A three-box happy path is faster to draw in Flowchart Builder than to encode and render. Reserve diagrams-as-code for the cases where you'll edit the source again — and use Markdown Preview to verify how the embedded blocks will look before you push.

The Practical Takeaway

Pick Mermaid by default if your documentation lives in Markdown — the GitHub-native rendering removes more friction than the layout engine costs you. Switch to PlantUML when you need UML semantics or richer sequence diagrams in a formal documentation system. Reach for Graphviz when the artifact is a graph and the layout matters more than the document it lives in. The tools are not in competition — they are tuned for different documents, and the smart move is to learn the syntax of all three at a basic level so you can match the tool to the situation rather than forcing every diagram through whichever one you happened to pick first.

FAQ

Does GitHub really render Mermaid inline in 2026?

Yes, since February 2022. Any fenced code block with mermaid as the language renders as an SVG diagram in any GitHub-flavored Markdown context — README files, issues, pull request descriptions, comments, wikis. GitLab added the same support around the same time, and Notion, Obsidian, and Quartz also render Mermaid natively. PlantUML and Graphviz still require a server or plugin.

Why does my 50-node Mermaid diagram look like spaghetti?

Mermaid uses Dagre for layout, which works well up to about 20–30 nodes and starts producing crossing edges, awkward spacing, and label collisions beyond that. There's no real fix at the Mermaid layer; you can nudge with subgraphs and ranks but never truly control it. For larger diagrams, switch to Graphviz (better algorithms) or split the diagram into multiple smaller ones (better readability anyway).

Can I use PlantUML without running a Java server?

Sort of. The official PlantUML server is Java, but there are wrappers — PlantUML.com hosts a public renderer, and some VS Code extensions bundle a JVM-free renderer for a subset of features. For serious use, expect to run a PlantUML server (Docker image is ~200 MB). If you're avoiding Java entirely, Mermaid covers most of PlantUML's common use cases with no runtime requirement.

What's the difference between dot, neato, and fdp in Graphviz?

They're different layout algorithms. dot is hierarchical (top-to-bottom, good for flowcharts and trees). neato is force-directed (good for undirected networks). fdp is a faster force-directed variant for large graphs. circo arranges nodes in a circle (good for small cyclic graphs). sfdp handles graphs with thousands of nodes. Pick based on shape: trees → dot, networks → neato, large dense graphs → sfdp.

Should I commit the rendered SVG/PNG or just the source?

Commit only the source (.mmd, .puml, .dot) for diagrams that render inline (Mermaid on GitHub). Commit both source and rendered PNG/SVG for diagrams that don't render natively (PlantUML, Graphviz) so readers see the picture without running the build. The rendered file is documentation; the source is editable. Treat them like generated CSS — the build artifact is committed alongside the source.

Can I use Mermaid for sequence diagrams instead of PlantUML?

Yes, and it's good enough for most cases. Mermaid sequence diagrams cover participants, messages, activations, notes, loops, and alt blocks — the 90% case for protocol or API diagrams. PlantUML wins on rich UML semantics (lifelines with destruction, references between diagrams, parallel groups) and on print-quality output. For a README or design doc, Mermaid; for a formal architecture record, PlantUML.

What happens when GitHub adds new Mermaid features mid-year?

GitHub bumps the Mermaid version periodically, which sometimes breaks diagrams that worked before — usually edge cases involving subgraphs, themes, or experimental diagram types. The Mermaid team maintains a changelog per release. If a diagram suddenly stops rendering, check the version bump first; pinning to a known-good Mermaid version is rarely necessary for typical flowcharts.

How do I keep diagrams in sync with code that changes?

The honest answer: you can't, fully. Diagrams-as-code reduces drift but doesn't eliminate it — when a service gets renamed, someone still has to update the diagram. Some teams generate diagrams from code (TypeScript AST → Mermaid, OpenAPI → PlantUML) so the source of truth is always the code. For diagrams that describe abstract concepts (state machines, decision trees), manual updates are unavoidable; review them as part of the same PR that changes the underlying behavior.