Three tools do the same job. You describe a diagram in text, and something else decides where the boxes go. Most comparisons of them collapse into an argument about syntax, which is the least interesting axis available — syntax you learn in an afternoon. Layout engines, rendering pipelines, and where the file sits in your workflow are what you actually live with.
Diagram as code is the practice of writing a diagram as plain text and letting a layout engine place the shapes, trading control over where each box sits for the ability to diff it, review it in a pull request, and regenerate it from source.
My opinions here were paid for on one side only. Codelit started as a prompt that emitted Mermaid, and I later wrote a parser that turns Mermaid text into an interactive React Flow canvas, so the Mermaid half of this is first-hand. For PlantUML and D2 I am reasoning from their documentation and their release history, cited inline with dates so you can check me.
Disclosure before the comparison, because it changes how you should read the rest. I build Codelit, an AI tool that draws architecture diagrams, and it loses to all three of these tools on the exact axis this post turns on: none of Codelit lives in your repository. A .mmd, .puml or .d2 file is text you own. It diffs, it survives me shutting the product down, and it costs nothing. Codelit's canvas is hosted, behind an account, with a paid tier above the free one. If your requirement is that the diagram be reviewable in a pull request and readable in ten years, the three text formats win and I am not going to pretend otherwise.
Which one should you actually pick?
Pick by where the diagram has to render and who maintains the rendering pipeline, not by which syntax reads better. Those two questions eliminate two of the three options in almost every case, and the syntax argument never gets to happen.
| What you are drawing | Pick | Why it wins | What you give up |
|---|---|---|---|
| A flow in a README or a PR description | Mermaid | GitHub renders the fenced block with zero setup | Layout control — you get what the renderer decides |
| A sequence diagram in an ADR | Mermaid or PlantUML | Neither needs Graphviz for sequence diagrams | Nothing much; this is the one genuine coin flip |
| A C4 model with vendor icons | PlantUML | The standard library ships C4 macros plus official AWS and Azure sprites | A JVM in your toolchain, and Graphviz for several diagram types |
| A dense architecture diagram with nested containers | D2 | Nested containers plus a choice of dagre, ELK or TALA | Native rendering anywhere, and someone has to own the build step |
| Anything a model writes for you unattended | Mermaid | Far more of it in the training corpus, and hosts render it or fail loudly | You inherit whichever Mermaid version the host happens to run |
| Something people click, edit, and export | None of the three | They are all static images by design | This is the gap the product I build sits in, and it is a real trade |
The last row is the honest boundary. All three of these produce a picture. None of them knows that a PostgreSQL node implies a subnet group and a parameter group, which is why turning a diagram into runnable Terraform and Kubernetes manifests is a different product category rather than a plugin.
Which syntax do models generate reliably, and which do they invent?
Mermaid, by a distance, and the reason is corpus size rather than syntax quality. GitHub has rendered Mermaid in Markdown since 14 February 2022, per its own announcement, which put several years of READMEs, issues and gists full of valid Mermaid into public view. PlantUML has a large corpus too, but it is older and skews toward classical UML. D2 is the youngest of the three and there is simply less of it about. That is a judgement about training data, not a measurement, and you should treat it as one.
Here is the part I paid for. Codelit's first version was a text box, a button, and GPT-3.5 asked to emit Mermaid. The generated Mermaid was wrong about 40% of the time — invalid syntax, missing semicolons, node references that pointed at nothing. I spent more time debugging generated Mermaid than I had ever spent dragging boxes in Lucidchart, which was the thing I built the product to escape.
Two caveats on that number, both of which cut against me. It is a GPT-3.5-era figure and frontier models today are dramatically better at structured text output. And it measures Mermaid only — I have never run the same experiment against PlantUML or D2, so I cannot tell you the comparative rate and I am not going to invent one.
What I can tell you is the fix, because it generalises to all three formats. Stop asking the model for rendering syntax. Ask for structured data with a schema you validate, then generate the syntax from that data yourself. A model that hallucinates an edge in JSON gives you a validation error you can retry on. A model that hallucinates an edge in Mermaid gives you a diagram that renders and is quietly wrong.
Where does each layout engine give up as the node count grows?
Layout is where all three of these actually differ, and the answer is different for each because none of them ships a single engine. Mermaid uses dagre for flowcharts and fcose for architecture diagrams, PlantUML shells out to Graphviz for seven diagram types, and D2 lets you choose between dagre, ELK and TALA.
| Tool | Default engine | Alternatives | What the alternative costs |
|---|---|---|---|
| Mermaid flowchart | dagre | ELK via @mermaid-js/layout-elk | Since v11 the minified bundle no longer contains ELK, so you install and register a separate ESM package |
| Mermaid architecture | fcose, force-directed | None | Four tuning knobs, exposed in v11.15.0, and a documented case they cannot fix |
| PlantUML | Graphviz dot | Smetana, an internal Java port | Available since 1.2021.5 for supported types, at some cost to layout quality |
| D2 | dagre | ELK, TALA | ELK is free; TALA is proprietary and watermarks output without a licence |
Two things in that table are worth pulling out.
The first is that D2's best answer for architecture diagrams is the one you have to pay for. Terrastruct built TALA specifically for software architecture layout and deliberately kept it closed while D2 itself stays open source. Free evaluation renders a watermark. If you adopt D2 for architecture work and then discover dagre and ELK are not laying out your containers the way you want, the escape hatch has a price tag on it.
The second is that Mermaid's newest architecture diagram type is force-directed, and force-directed layout is the thing I ripped out of my own product. Attempt two of six layout rewrites was d3-force. The problem was never aesthetics — it was that the same input produced different node positions on every run, and users read non-determinism as the tool being broken. I settled on dagre with nodesep at 80 and ranksep at 100, and stopped touching it.
fcose is not raw d3-force. It is compound-aware and seeded spectrally, which is a meaningfully better starting point. But the practical advice falls out anyway: if you want a Mermaid diagram whose layout is stable across renders, flowchart sits on dagre and architecture-beta does not.
What can Mermaid's architecture diagram actually draw?
Four primitives, plus icons. Mermaid's architecture diagrams, documented from v11.1.0 onward and still spelled architecture-beta, give you groups, services, edges and junctions, with edges that attach to a named side of a node rather than wherever the renderer feels like.
The syntax is compact. Groups and services take an optional icon and an optional parent, written as service id(icon)[title] in parent. Edges name a side on each end using T, B, L or R, so you can force a connection to leave the bottom of one box and enter the left of another. Junctions act as a four-way split point for edges that need to fan out cleanly. Version 11.15.0 exposed four fcose knobs — nodeSeparation, idealEdgeLengthMultiplier, edgeElasticity and numIter — and 11.16.0 added an align directive for rows and columns.
Out of the box you get five icons: cloud, database, disk, internet and server. Anything beyond that means registering an icon pack, which Mermaid supports through iconify JSON packs.
Now the limitation, which the documentation states plainly and which no amount of tuning fixes. When two siblings share the same logical position in the spatial map they can render on top of each other, tracked as issue 6120 in the Mermaid repository, and the docs say directly that no combination of the layout knobs will move them apart. The align directive is the workaround. That is a fine state for a diagram type carrying a beta suffix, and it is not a fine state for the canonical architecture diagram of a system you are on call for.
Why rendering performance is not one number
Because the three tools run in three different places, and a benchmark comparing them measures three unrelated things. Mermaid is JavaScript that runs in the reader's browser at view time. PlantUML is a Java process that produces an image, often with a Graphviz subprocess underneath. D2 is a Go binary you run at build time.
| Tool | What actually runs | Where the cost lands | What the reader downloads |
|---|---|---|---|
| Mermaid | JavaScript in the page, or headless via mermaid-cli | The reader's device, on every page view | Your source text plus the Mermaid bundle |
| PlantUML | A JVM, plus Graphviz for seven diagram types | Your CI job or a rendering server | A finished image |
| D2 | A Go CLI, ahead of time | Your build | A finished image or SVG |
That table is the useful comparison, and it answers the question people are usually really asking. If your docs site has thirty diagrams on one page, Mermaid charges every visitor for all thirty. If your CI is already slow, PlantUML's JVM start and Graphviz call land there instead. Pick where you would rather pay.
I do not have measured render times for the same architecture across all three, so I am not going to quote any. Anyone who does quote them owes you their node count, their diagram type and their layout engine, because changing any one of those three moves the answer more than changing the tool does.
What happens to these diagrams in code review?
Mermaid is the only one of the three a GitHub reviewer sees without leaving the review. GitHub's documentation lists four renderable diagram syntaxes in Markdown — mermaid, geoJSON, topoJSON and ASCII STL — and PlantUML and D2 are not among them.
| Where the diagram lives | Mermaid | PlantUML | D2 |
|---|---|---|---|
| GitHub README, issue, PR | Rendered inline, no setup | Needs a committed image or a rendering proxy | Needs a committed image |
| GitLab | Rendered inline | Rendered inline; self-managed needs an admin-configured server | Needs a committed image |
| A static docs site you control | A client-side bundle | A build step | A build step |
| The diff itself | Readable text | Readable text | Readable text |
All three diff well, which is the whole point of writing diagrams as text. The difference is what a reviewer has to do to see the picture. Committing a rendered SVG next to the source solves it and creates a second problem: every diagram change now produces two diffs, one a reviewer can read and one they will scroll past, and the two can drift apart the first time somebody edits the source and skips the render step.
If you go the committed-image route, make the render a CI check that fails when the image and the source disagree. Otherwise you have built a documentation system whose failure mode is silent.
What I would put in a repo I maintain
Mermaid for anything that lives in a README or a pull request, and flowchart rather than architecture-beta whenever layout stability matters more than cloud-shaped icons. PlantUML when the diagram is genuinely UML or C4 and there is already a JVM in the pipeline. D2 when someone owns the build step and the diagram is dense enough that nested container layout is the actual problem.
One structural cost applies only to Mermaid and it is easy to miss: you do not control which version renders your file on somebody else's site. A diagram that works on the current release can fail on a host pinned to an older one, and the failure surfaces to your readers rather than to you. PlantUML and D2 hand you an image, and an image renders the same everywhere forever.
The format decision is smaller than it feels. A Mermaid block nobody has updated in eight months is worth less than a D2 file with a CI job that fails when the diagram and the code disagree, and no comparison table will tell you which of those two you are going to build. That part is a workflow choice, and it is the only part of this that survives the next release of any of these tools.
Questions people actually ask
- Is Mermaid or PlantUML better for architecture diagrams?
- It depends on where the diagram has to render. Mermaid wins when the diagram lives in a GitHub README or a pull request, because GitHub renders it inline with no setup at all. PlantUML wins when the diagram is UML or C4, because its standard library ships C4 macros and official AWS and Azure sprite sets, and because its layout for class and component diagrams comes from Graphviz, which has had decades of tuning behind it.
- Does GitHub render PlantUML or D2 diagrams in Markdown?
- No. GitHub's documentation states that you can create diagrams in Markdown using four syntaxes, and they are mermaid, geoJSON, topoJSON and ASCII STL. PlantUML and D2 both need a rendering step in continuous integration that commits an image alongside the source, or a proxy server that renders on request. GitLab differs here: it renders PlantUML blocks natively on GitLab.com, and on self-managed instances once an administrator configures a PlantUML server.
- Does PlantUML still need Graphviz installed?
- For several diagram types, yes. PlantUML's own Graphviz page lists use case, class, object, component, deployment, state and legacy activity diagrams as requiring it, and says Graphviz is neither needed nor used for anything else, which covers sequence, timing, gantt, mindmap, JSON and the rest. Since version 1.2021.5 you can add a pragma that switches supported types to Smetana, an internal Java port of Graphviz, at some cost to layout quality.
- Which diagram syntax do large language models generate most reliably?
- Mermaid, and the reason is corpus size rather than syntax elegance. GitHub has rendered Mermaid in Markdown since February 2022, so there is an enormous amount of it in public repositories for models to have learned from. The safer pattern regardless of tool is to never ask a model for rendering syntax at all. Ask for structured data, validate it, and generate the syntax yourself.
- Is D2 still actively maintained?
- D2's most recent tagged release on GitHub is version 0.7.1, published on the nineteenth of August 2025. For comparison, Mermaid shipped 11.16.0 and PlantUML shipped version 1.2026.6 in June 2026. A quiet year is not abandonment, and the language is open source and stable, but release cadence is a fair thing to weigh when you are choosing a format for a repository you intend to maintain for years.
- What can Mermaid's architecture diagram type not do?
- Its documentation names one limitation directly. When two sibling nodes share the same logical position in the spatial map, they can render on top of each other, and no combination of the exposed layout knobs will separate them. That is tracked as issue 6120 in the Mermaid repository. The alignment directive added in version 11.16.0 exists partly to work around it. The diagram type also still carries the beta suffix in its keyword.