d2 Documentation

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

RAW Doc

Technical Documentation: d2lang/d2

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (README.md)

Table of Contents

What does D2 look like?

d2
vars: {
  d2-config: {
    layout-engine: elk
    # Terminal theme code
    theme-id: 300
  }
}
network: {
  cell tower: {
    satellites: {
      shape: stored_data
      style.multiple: true
    }

    transmitter

    satellites -> transmitter: send
    satellites -> transmitter: send
    satellites -> transmitter: send
  }

  online portal: {
    ui: {shape: hexagon}
  }

  data processor: {
    storage: {
      shape: cylinder
      style.multiple: true
    }
  }

  cell tower.transmitter -> data processor.storage: phone logs
}

user: {
  shape: person
  width: 130
}

user -> network.cell tower: make call
user -> network.online portal.ui: access {
  style.stroke-dash: 3
}

api server -> network.online portal.ui: display
api server -> logs: persist
logs: {shape: page; style.multiple: true}

network.data processor -> api server

Open in playground

For more examples, see ./docs/examples.

Quickstart

The most convenient way to use D2 is to just run it as a CLI executable to
produce SVGs from .d2 files.

sh
# First, install D2
curl -fsSL https://d2lang.com/install.sh | sh -s --

echo 'x -> y -> z' > in.d2
d2 --watch in.d2 out.svg

A browser window will open with out.svg and live-reload on changes to in.d2.

Install

The easiest way to install is with our install script:

sh
curl -fsSL https://d2lang.com/install.sh | sh -s --

You can run the install script with --dry-run to see the commands that will be used
to install without executing them.

Or if you have Go installed you can install from source though you won't get the manpage:

sh
go install github.com/d2lang/d2@latest

You can also install a release from source which will include manpages.
See ./docs/INSTALL.md#source-release.

To uninstall with the install script:

sh
curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall

For detailed installation docs, see ./docs/INSTALL.md.
We demonstrate alternative methods and examples for each OS.

As well, the functioning of the install script is described in detail to alleviate any
concern of its use. We recommend using your OS's package manager directly instead for
improved security but the install script is by no means insecure.

D2 as a library

In addition to being a runnable CLI tool, D2 can also be used to produce diagrams from
Go programs.

For examples, see ./docs/examples/lib.

Themes

D2 includes a variety of official themes to style your diagrams beautifully right out of
the box. See ./d2themes to browse the available themes and make or
contribute your own creation.

Fonts

D2 ships with "Source Sans Pro" as the font in renders. If you wish to use a different
one, please see ./d2renderers/d2fonts.

Export file types

D2 supports SVG, PNG, GIF, PDF, and PPTX exports.

Language tooling

D2 is designed with language tooling in mind. D2's parser can parse multiple errors from a
broken program, has an autoformatter, syntax highlighting, and we have plans for LSP's and
more. Good language tooling is necessary for creating and maintaining large diagrams.

The extensions for VSCode and Vim can be found in the Related section.

Layout engines

D2 bundles three layout engines. Select one with --layout=dagre, --layout=elk, or
--layout=tala. You can also set D2_LAYOUT or vars.d2-config.layout-engine in D2 source.
Run d2 layout to list the engines or d2 layout <name> for their configuration options.

  • Dagro (default, bundled): A native Go port of the
    Dagre directed graph layout engine that produces layered/hierarchical layouts. Based
    on Graphviz's DOT algorithm.
  • elk-go (bundled): A native Go port of the ELK
    directed graph layout engine, particularly suited for node-link diagrams with an
    inherent direction and ports.
  • TALA (bundled, opt-in): Native layout and edge-routing engine designed
    specifically for software architecture diagrams. Select it with --layout=tala,
    D2_LAYOUT=tala, or a layout-engine: tala setting under vars.d2-config in D2 source.

D2 also handles specialized layouts such as sequence diagrams and grids. Go applications
can use the layout packages directly or provide layout and routing functions
through d2lib.

Sketch-mode rendering uses rough-go, a native Go
compatibility port of the Rough.js 4.6.6 rendering surface used by D2.

LaTeX labels use mathjax-go, a native Go port of
the MathJax 3.2.2 TeX-to-SVG pipeline.

Comparison

For a comparison against other popular text-to-diagram tools, see
https://text-to-diagram.com.

Contributing

Contributions are welcome! See ./docs/CONTRIBUTING.md.

License

Open sourced under the Mozilla Public License 2.0. See ./LICENSE.txt.

We are constantly working on new plugins, integrations, extensions. Contributions are
welcome in any official or community plugins. If you have somewhere in your workflow that
you want to use D2, feel free to open a discussion. We have limited bandwidth and usually
choose the most high-demand ones to work on. If you make something cool with D2 yourself,
let us know and we'll be happy to include it here!

Official plugins

Community plugins

Misc

FAQ

  • Does D2 collect telemetry?
    • No, D2 does not use an internet connection after installation, except to check for
      version updates from Github periodically.
  • Does D2 need a browser to run?
    • No, D2 can run entirely server-side.
  • What's coming in the next release?
  • I have a question or need help.
    • The best way to get help is to ask on D2 Discord
  • I have a feature request, proposal, or bug report.
    • Please open up a Github Issue.
  • I have a private inquiry.

Notable open-source projects documenting with D2

Do you have or see an open-source project with .d2 files? Please submit a PR adding to
this selected list of featured projects using D2.


Examples Lib README (`docs/examples/lib/README.md`)

D2 library examples

We have a few examples in this directory on how to use the D2 library to turn D2 scripts
into rendered svg diagrams and more.

Each example is runnable though does not include error handling for readability.

[./1-d2lib](./1-d2lib)

A minimal example showing you how to compile the diagram x -> y into an svg.

[./2-d2oracle](./2-d2oracle)

D2 is built to be hackable -- the language has an API built on top of it to make edits
programmatically.

Modifying the previous example, this example demonstrates how
d2oracle can be used to create a new shape, style it programmatically
and then output the modified d2 script.

This makes it easy to build functionality on top of D2. Terrastruct uses the
d2oracle API to implement editing of D2 from mouse actions in a
visual interface.

[./3-lowlevel](./3-lowlevel)

d2lib from the first example is just a wrapper around the lower level APIs. They
can be used directly and this example demonstrates such usage.

This shouldn't be necessary for most use cases.


CONTRIBUTING (`docs/CONTRIBUTING.md`)

Contributing

Welcome

D2's long-term mission is to significantly reduce the
amount of time and effort it takes to create and maintain high-quality diagrams for every
software team. We started this because we love the idea of creating diagrams with text --
but it was clear the existing solutions were inadequete in their state and speed of
execution for this idea to be widely usable.

We've tried our best to avoid the mistakes of the past and take inspiration from the most
successful modern programming and configuration languages. D2 has built up each step of
the text-to-diagram pipeline from scratch, rethinking each one from first principles, from
the dead simple syntax, to the readable compiler, our own SVG renderer, etc.

D2 is committed to making something people want to use. That means contributions don't
only have to be in the form of pull requests. Your bug reports, plugins, examples,
discussions of new ideas, help a great deal.

If you'd like to get involved, we're also committed to helping you merge that first pull
request. You should be able to freely pick up Issues tagged as "good first issue". Those
will usually include notes on getting started, but if they're missing or insufficient,
just leave a comment and we'll add more detail. If you need help getting started, please
don't hesitate to pop into Discord -- as long as you want to help, we'll find the perfect
task (complexity matches your appetite and programming experience, in an area you're
interested in, etc).

CI

Most of D2's CI is open sourced in its own
repository, included as a submodule. After you clone
D2, make sure you initialize the submodules:

sh
git submodule update --init --recursive

./make.sh runs the standard checks, including JS and Go WASM tests. Install the
Go version from go.mod, the Bun version from d2js/js/package.json, and Node.js
24 with npm. Subcommands to run individual parts of the CI:

  • ./make.sh fmt
  • ./make.sh lint
  • ./make.sh test
  • ./make.sh test-wasm
  • ./make.sh race
  • ./make.sh build

Here's what a successful run should look like:

Please make sure CI is passing for any PRs submitted for review.

Be sure to update the submodule whenever there are changes:

sh
git submodule update --recursive

Logistics

  • Use Go 1.22.
  • Please sign your commits
    (https://github.com/terrastruct/d2/pull/557#issuecomment-1367468730).
  • D2 uses Issues as TODOs. No auto-closing on staleness.
  • Branch off master.
  • If there's an Issue related, include it by adding "[one-word] #[issue]", e.g. "Fixes
    #123" somewhere in the description.
  • Whenever possible and relevant, include a screenshot or screen-recording.

Dev

Content

Unless you've contributed before, it's safest to choose an Issue with a "good first issue"
label. If you'd like to propose new functionality or change to current functionality,
please create an Issue first with a proposal. When you start work on an Issue, please
leave a comment so others know that it's being worked on.

Tests

D2 has extensive tests, and all code changes must include tests.

With the exception of changes to the renderer, all code should include a package-specific
test. If it's a visual change, an end-to-end (e2e) test should accompany.

Let's say I make some code changes. I can easily see how this affects the end result by
running:

text
./ci/e2ereport.sh -delta

This gives me a nice HTML output of what the test expected vs what it got (this was a PR
fixing multi-byte character labels):

Run ./ci/e2ereport.sh -help for flags, including how to get deltas for a single test.

If you're testing labels and strings, it's encouraged to use 1-letter strings (x) in small
functional tests to minimally pinpoint issues. If you are testing something that exercises
variations in strings, or want to mimic more realistic diagram text, it's encouraged you
generate random strings and words from fortune. It gives a good range of the English
language. (Sometimes it gives controversial sentences -- don't use those.)

Script to generate one line of random text:

text
ipsum1() {
  fortune | head -n1 | sed 's/^ *//;s/ *$//' | tr -d '\n' | tee /dev/stderr | pbcopy
}

Running tests

Run: ./ci/test.sh

The weekly CI workflow runs tests with -race -count=1 to catch race conditions
without using cached test results. Run the same check locally with
./ci/test.sh -race -count=1 ./..., or use ./make.sh race during development.

If you add a new test and run, it will show failure. That's because the vast majority of
D2's tests are comparing outputs. You don't define the expected output manually. The
testing library generates it and it's checked into version control if it looks right. So
for the first run of a new test, it has no expected output, and will fail. To accept the
result as the expected, run the test with environment variable TESTDATA_ACCEPT=1.

Chaos tests

D2 has chaos tests which produce random
configurations of diagrams. It can be helpful to run a few iterations (N~=100) to spot
cover your manual tests.

D2_CHAOS_MAXI=100 D2_CHAOS_N=100 ./ci/test.sh ./d2chaos

Documentation

The code itself should be documented as appropriate with Go-style comments. No rules here,
GetX() doesn't need a // GetX gets X.

If it's some new functionality, please submit a pull request to document it in the
language docs:
https://github.com/d2lang/d2-docs.

Questions

If you have any questions or would like to get more involved, feel free to open an issue
to discuss publicly, or chat in Discord! If you have a
private inquiry, feel free to email us at [email protected].


INSTALL (`docs/INSTALL.md`)

install

You may install d2 through any of the following methods.

install.sh

The recommended and easiest way to install is with our install script, which will detect
the OS and architecture you're on and use the best method:

sh
# With --dry-run the install script will print the commands it will use
# to install without actually installing so you know what it's going to do.
curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run
# If things look good, install for real.
curl -fsSL https://d2lang.com/install.sh | sh -s --

For help on the terminal run, including the supported package managers and detection
methods:

sh
curl -fsSL https://d2lang.com/install.sh | sh -s -- --help

Security

The install script is not the most secure way to install d2. We recommend that if
possible, you use your OS's package manager directly or install from source with go as
described below.

But this does not mean the install script is insecure. There is no major flaw that
the install script is more vulnerable to than any other method of manual installation.
The most secure installation method involves a second independent entity, i.e your OS
package repos or Go's proxy server.

We're careful shell programmers and are aware of the many footguns of the Unix shell. Our
script was written carefully and with detail. For example, it is not vulnerable to partial
execution and the entire script runs with set -eu and very meticulous quoting.

It follows the XDG standards, installs d2 properly into a Unix hierarchy path
(/usr/local unless /usr/local requires sudo in which case ~/.local is used) and
allows for easy uninstall. You can easily adjust the used path with --prefix.

Some other niceties are that it'll tell you if you need to adjust $PATH or $MANPATH to
access d2 and its manpages. For releases that bundle TALA, select it with
d2 --layout=tala; run d2 layout to check the installed engines.
This installer installs only D2 and rejects the obsolete --tala option before
installation. Omit that option when installing D2. Older D2 releases require the
separate plugin available from the legacy TALA releases.
You can install a specific version of d2 with --version. Run the script with
--help for detailed docs on its options and features.

If you're still concerned, remember you can run with --dry-run to avoid writing anything.

For standalone installations, the script obtains the selected archive's SHA-256 digest
from the GitHub Releases API and, when GitHub provides one, verifies both cached and newly
downloaded archives before extracting them. Historical releases for which GitHub reports
no digest remain installable with a warning. Releases produced by the current release
workflow also include SHA256SUMS and GitHub artifact attestations. You can verify build
provenance separately with:

sh
gh attestation verify d2-v0.0.0-linux-amd64.tar.gz --repo d2lang/d2

macOS (Homebrew)

D2 binaries built with Go 1.27 require macOS 13 Ventura or newer.

If you're on macOS, you can install with brew.

sh
brew install d2

The install script above does this automatically if you have brew installed and
are running it on macOS.

You can also install from source with:

d2
brew install d2 --HEAD

Linux

The following distributions have packages for d2:

Void Linux

All supported platforms:

sh
xbps-install d2

Standalone

We publish standalone release archives for every release on Github.

Here's a minimal example script that downloads a standalone release, extracts it into the
current directory and then installs it.
Adjust VERSION, OS, and ARCH as needed.

sh
VERSION=v0.0.13 OS=macos ARCH=amd64 curl -fsSLO \
    "https://github.com/d2lang/d2/releases/download/$VERSION/d2-$VERSION-$OS-$ARCH.tar.gz" \
    && tar -xzf "d2-$VERSION-$OS-$ARCH.tar.gz" \
    && make -sC "d2-$VERSION" install

To uninstall:

sh
VERSION=v0.0.13 make -sC "d2-$VERSION" uninstall

Manual

You can also manually download the .tar.gz release for your OS/ARCH combination and then
run the following inside the extracted directory to install:

sh
make install

Run the following to uninstall:

sh
make uninstall

PREFIX

You can control the Unix hierarchy installation path with PREFIX=. For example:

sh
# Install under ~/.local.
# Binaries will be at ~/.local/bin
# And manpages will be under ~/.local/share/man
# And supporting data like icons and fonts at ~/.local/share/d2
make install PREFIX=$HOME/.local

The install script places the standalone release into $PREFIX/lib/d2/d2-<version>
and we recommend doing the same with manually installed releases so that you
know where the release directory is for easy uninstall.

From source

You can always install from source:

sh
go install github.com/d2lang/d2@latest

You need at least Go v1.27

Source Release

To install a release from source clone the repository and then:

sh
./ci/release/build.sh --install
# To uninstall:
# ./ci/release/build.sh --uninstall

Installing a real release will also install manpages and in the future other assets like
fonts and icons. Furthermore, when installing a non versioned commit, installing a release
will ensure that d2 --version works correctly by embedding the commit hash into the d2
binary.

Remember, you need at least Go v1.27

Windows

We have prebuilt releases of d2 available for Windows via .msi installers. The installer
will add the d2 binary to your $PATH so that you can execute d2 in cmd.exe or
pwsh.exe.

Release archives

We also have release archives for Windows structured in the same way as our Unix releases
for use with MSYS2.

See MSYS2 or Git Bash (Git
Bash is based on MSYS2).

MSYS2 provides a unix style shell environment that is native to Windows (unlike
Cygwin). MSYS2 allows install.sh to work, enables automatic
installation of our standalone releases via make install and makes the manpage
accessible via man d2.

The MSYS2 terminal also enables d2 to display colors like in the above screenshot.

In addition, all of our development and CI scripts work under MSYS2 whereas they do not
under plain Windows.

WSL

d2 works perfectly under WSL
aka Windows Subsystem for Linux if that's what you prefer. Installation is just like any
other Linux system.

Native Package managers

The following are community-managed Windows releases of d2 on popular package managers

Scoop

sh
scoop install main/d2

Chocolatey

sh
choco install d2

Docker

The canonical image is d2lang/d2.
terrastruct/d2 remains available as a maintained compatibility mirror for existing
installations, but new usage should use d2lang/d2.

We publish amd64 and arm64 images based on debian:latest for each release.

Example usage:

sh
echo 'x -> y' >helloworld.d2
docker run --rm -it -u "$(id -u):$(id -g)" -v "$PWD:/home/debian/src" \
  -p 127.0.0.1:8080:8080 d2lang/d2:v0.1.2 --watch helloworld.d2
# Visit http://127.0.0.1:8080

Coming soon

  • rpm and deb packages
    • with repositories and standalone
  • homebrew core

--- METRICS ---