Library / SDK
d2lang/d2 avatar
d2lang/d2

D2: a text-to-diagram language with a pluggable layout engine

D2 is a modern diagram scripting language that turns text to diagrams.

25,425 stars752 forksGoMPL-2.0

At a glance

What is it?
D2 compiles .d2 source files into SVG, PNG, GIF, PDF or PPTX through a Go CLI or as a library. Its layout engine is swappable, which is the main design decision to weigh before adopting it.
Who is it for?
Adopt D2 if your diagrams live in a repository next to the code they describe and you want them regenerated from a diffable .d2 file. Do not adopt it if you need a hand-placed canvas, because the layout is computed by the engine, not by you.
Can I use it commercially?
Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem D2 solves: diagrams that drift from the code

Diagram files produced by a drawing tool are binary or XML blobs. Nobody reviews them in a pull request, and nobody notices when they stop matching the system. D2 takes the opposite position: the diagram is a text file with a .d2 extension, and the picture is an artifact you regenerate. The README's framing is "a modern diagram scripting language that turns text to diagrams", and the quickstart is three lines long: write x -> y -> z into in.d2, run the CLI, get out.svg.

The intended user is a developer documenting software architecture, which is why the repository carries the software-architecture topic and why one of the bundled layout engines is described as designed specifically for software architecture diagrams. The language tooling section makes the same case from the maintenance side: the parser reports multiple errors from a broken program, there is an autoformatter, and syntax highlighting exists. Those are features you need when a diagram has a hundred nodes, not when it has five. If your diagrams are marketing illustrations, this is not the tool.

What a .d2 file actually contains

D2 mixes a container syntax with attribute blocks. The README example opens with a vars block holding d2-config, where layout-engine and theme-id are set, then declares a network container with nested containers inside it (cell tower, online portal, data processor). Nodes are bare identifiers, so writing transmitter on its own line creates a node. Shapes are attributes: shape: stored_data, shape: cylinder, shape: hexagon, shape: person, shape: page. Repeating a visual element is style.multiple: true.

Edges are written with an arrow and an optional label after a colon, as in satellites -> transmitter: send. The same edge can appear more than once in the example, which is how the source expresses parallel connections. Style attributes attach to an edge in a block, for instance style.stroke-dash: 3 on the access edge. Node dimensions are settable too: the user node carries width: 130.

Two syntax details are worth noticing because they shape how you organize a file. Container paths are addressable with dots, so an edge can be declared from cell tower.transmitter to data processor.storage without re-declaring either node. And the vars block at the top is where global configuration lives, which means a diagram's layout engine can be pinned in the source rather than passed on the command line. That is convenient for reproducibility and awkward when the same file is rendered in two different pipelines.

Layout engines: Dagro by default, elk-go and TALA opt-in

The plugin system is the part of D2 that most affects output quality. Three layout engines are listed. Dagro is the default and bundled: a native Go port of the Dagre directed graph layout engine producing layered, hierarchical layouts, based on Graphviz's DOT algorithm. elk-go is also bundled and is a native Go port of the ELK engine, described as suited to node-link diagrams with an inherent direction and ports. TALA is bundled but opt-in, and the README calls it a native layout and edge-routing engine designed specifically for software architecture diagrams.

The selection mechanism is explicit: --layout=tala on the command line, the D2_LAYOUT=tala environment variable, or layout-engine: tala under vars.d2-config in the source. Three ways to set the same thing is one more than most projects need, and the precedence between them is not stated in the material I have. If you pin the engine in the file and also pass --layout, verify which wins before you rely on it in CI.

Because the engines are ports rather than wrappers, D2 does not shell out to Graphviz or a Java ELK process for these three. The README does say the project intends to integrate with other layout engines such as dot, and with single-purpose layout types like sequence diagrams, so the current set is a starting point rather than a fixed boundary. The plugin system also allows the rendering pipeline itself to be customized, and plugins can be bundled with the build or installed as a standalone binary.

Getting it running, and the install script caveat

The documented install path is a shell script: curl -fsSL https://d2lang.com/install.sh | sh -s --. The README notes you can add --dry-run to see the commands without executing them, and --uninstall to remove the installation. If Go is already on the machine, go install github.com/d2lang/d2@latest works but the README states you will not get the manpage; installing a source release does include manpages, with details in docs/INSTALL.md.

The project's own advice is worth repeating plainly: it recommends using your OS package manager directly instead for improved security, while stating the install script is by no means insecure and that its functioning is described in detail in the installation documentation. Piping a remote script into a shell is a decision your environment has to make, and D2 does not hide that.

Once installed, the working loop is d2 --watch in.d2 out.svg. The README says a browser window opens with out.svg and live-reloads when in.d2 changes. That flag is the reason to use the CLI rather than the playground for real work: you edit text on one side and watch the rendered diagram update. There is also a playground at play.d2lang.com, and the README example links to a playground URL with the script encoded in the query string, which is a practical way to share a diagram without shipping a file.

Export formats, themes and fonts

Exports are SVG, PNG, GIF, PDF and PPTX. That list matters more than it looks. SVG is the default in the quickstart and is the right target for documentation sites and READMEs. PPTX is the odd one out and is the format that decides the tool for anyone who has to hand a diagram to a slide deck; a text-to-diagram tool that only emitted SVG would push that work back to a screenshot. PNG and GIF cover raster needs, and PDF covers print.

Styling is split between themes and fonts. The README says D2 includes a variety of official themes, browsable in the d2themes directory, and that you can make or contribute your own. The example sets theme-id: 300 with a comment identifying it as a terminal theme, so themes are selected by numeric id. Fonts default to Source Sans Pro, and changing them is documented under d2renderers/d2fonts. Both directories are in the repository, which means theme and font definitions are source files you can read and edit rather than opaque settings.

The honest limitation here is that the README does not enumerate the theme ids or show what each one looks like. You browse the d2themes directory or the playground to find one. That is a documentation gap, not a design flaw, but it costs time on first setup.

D2 as a Go library, and what that changes

The CLI is not the only interface. The README states that D2 can be used to produce diagrams from Go programs, with examples under docs/examples/lib. This is the difference between a documentation tool and a build dependency: a Go service that generates architecture diagrams from its own service registry can call the library instead of writing .d2 files to disk and shelling out.

The same fact narrows the audience. If your stack is not Go, the library route is closed and you are consuming the CLI as a subprocess, which is fine but means you inherit process start-up and file I/O on every render. The README does not describe a language-agnostic API or a server mode, so treat the library as a Go-only convenience. There is also an npm package, @d2lang/d2, referenced in the badge row, but the README body does not document its API, so I cannot say what it exposes beyond the package existing.

Where D2 is the wrong tool

The layout is computed. You do not place boxes. That is the whole value proposition and also the constraint: if you need a diagram with a specific visual arrangement, one that a human would nudge into place, you will fight the engine. The three bundled engines give you three families of automatic layout, not manual control. For a poster, a one-off architecture illustration with deliberate negative space, or anything where the composition is the point, a canvas tool is faster.

There is a second failure mode in the plugin architecture. Layout engines can be installed as standalone binaries, which means a diagram that renders in one environment may render differently, or fail, in another if the engine is missing. The bundled set (Dagro, elk-go, TALA) avoids this, but the moment you install a third-party engine you have added a binary dependency that is not captured in the .d2 file. Pinning layout-engine in vars.d2-config documents the intent; it does not install the engine.

Finally, the project is pre-1.0. The release history shows v0.9.0, v0.8.2 and v0.7.1, with v0.9.0 dated 2026-09-07. The gap between v0.7.1 in August 2025 and v0.8.2 in August 2026 is a year, so the cadence is not fast. Treat the syntax as stable enough for internal documentation and check the changelog before assuming an upgrade is trivial.

How this differs from Mermaid and Graphviz

Mermaid is the closest comparison in intent: text in, diagram out, rendered in Markdown and wikis. The difference visible in this material is the engine layer. Mermaid's rendering is tied to its own layout implementation, while D2 exposes layout as a plugin boundary with three bundled engines and a documented selection mechanism (--layout, D2_LAYOUT, layout-engine). If Dagro's layered output does not suit a particular diagram, you can switch to elk-go or TALA without changing the source file's structure. The README also points to text-to-diagram.com for comparisons, which is the project's own maintained comparison page rather than a neutral one.

Graphviz is the older reference point, and D2's default engine is explicitly a Go port of the Dagre layout engine, which the README ties back to Graphviz's DOT algorithm. So the lineage is direct: if you already think in DOT, Dagro's output will look familiar. The difference is the language around it. DOT has no autoformatter story in the same way, and D2's parser is described as reporting multiple errors from a broken program, which shortens the edit-render loop. Graphviz remains the safer choice if you need a layout algorithm D2 does not bundle; D2's answer to that is the dot integration it says it intends to add, which is a plan, not a feature.

Licence and maintenance cost

D2 is MPL-2.0. That is file-level copyleft: modifications to files covered by the licence stay under it, while larger works that combine D2 with other code can be distributed under other terms. Using the CLI to render .d2 files into images is not the same activity as modifying D2's source files, and the two have different obligations. This is a summary of the licence family, not legal advice; read LICENSE.txt and get your own counsel if you plan to fork the renderer or ship a modified binary.

Maintenance cost splits into three parts. The .d2 files themselves are small text and diff cleanly, which is the cheap part. Themes and fonts are source directories you can edit, so custom styling is a file change rather than a plugin build, though it does mean tracking upstream changes to d2themes and d2renderers/d2fonts on upgrade. The expensive part, if you take it on, is the plugin boundary: a standalone layout engine binary is a build artifact you now have to version, distribute and troubleshoot independently of the D2 release you pair it with.

For a team that stays on the bundled engines and the CLI, the upgrade path is a binary swap plus a check of the changelog for syntax changes. The release cadence suggests that check is not urgent but should not be skipped.

Editorial conclusion

Adopt D2 if your diagrams live in a repository next to the code they describe and you want them regenerated from a diffable .d2 file. Do not adopt it if you need a hand-placed canvas, because the layout is computed by the engine, not by you. Before committing, verify three things: which layout engine your diagram type actually needs (Dagro, elk-go or TALA, selected with --layout or a layout-engine key under vars.d2-config), whether the export format you need is in the supported list (SVG, PNG, GIF, PDF, PPTX), and whether MPL-2.0 file-level copyleft is acceptable for how you plan to redistribute the binary or modified source files.

Official sources

  1. d2lang/d2 on GitHub
  2. License: MPL-2.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes