D2: A Modern Diagram Scripting Language Guide
D2 is a newer diagram-as-code language designed for readability and good-looking output. This guide covers its clean syntax for shapes, connections, containers, and styling.
D2 is a modern entrant in the diagram-as-code space, created to address a common complaint about older tools: that the syntax is fiddly and the default output looks dated. D2 was designed from the start for readable source and attractive, well-laid-out diagrams, with pluggable layout engines and a scripting language that feels natural to write. If Graphviz is the venerable workhorse and Mermaid the ubiquitous documentation format, D2 is the contemporary option that prioritises how the source reads and how the result looks.
This guide introduces D2's syntax - shapes, connections, labels, containers, and styling - and situates it among the other text-diagram languages. Like DOT and PlantUML, D2 is a text format, so you can reference a diagram written in it and rebuild it as an editable, collaborative diagram in Atlas Diagram Studio at /diagrams. For the broader philosophy, see the diagram-as-code guide at /guides/diagram-as-code-guide; for how the text-first approach compares to visual editing, see /guides/diagram-as-code-vs-gui-diagramming.
Shapes and connections
D2's core is disarmingly simple. Naming something creates a shape, so writing server on its own line draws a box labelled server. You connect two shapes with an arrow: server -> database draws a directed connection, and you can add a label to the connection with a colon, so server -> database: query reads as a labelled edge. This a -> b: label pattern is the heart of D2, and much of a diagram is just a list of such connections, which makes the source unusually close to how you would describe the diagram out loud.
D2 supports several connection styles. A plain -> is a directed arrow, <-> is bidirectional, -- is an undirected line, and <- points the other way. You can give a shape an explicit label different from its key by writing server: Web Server, which keeps a short identifier for connections while showing a friendly label. Shapes can take a type too - server.shape: cylinder or a person shape - so the box renders as the right symbol for a database, a queue, a person, or a cloud.
Containers and nesting
D2's container syntax is one of its most elegant features. You nest shapes using dot notation or braces, and the container is drawn automatically as a labelled box around its children.
- Dot notation: network.server and network.database place both shapes inside a network container without declaring it separately.
- Brace blocks: writing network with a brace body lets you declare the container and its children explicitly, with styling on the container.
- Cross-container connections: network.server -> dmz.gateway connects shapes that live in different containers, and D2 routes the edge between the boxes.
- Deep nesting: containers can hold containers, so cloud.region.service models a multi-level topology.
- Container labels: give a container a friendly label the same way as any shape, with a colon.
- Connections to a container itself, rather than a child, attach at the box boundary for higher-level relationships.
Labels, styling, and layout
Styling in D2 uses a nested style block on any shape or connection. Inside a shape's brace body you can add a style block that sets properties like fill, stroke, border-radius, opacity, and font-size, so a shape can be coloured and shaped without leaving the language. Connections take styles too, letting you dash a line, thicken it, or colour it to signal a particular kind of relationship. Because styling is part of the language rather than a separate stylesheet, a D2 file is self-contained.
Layout is where D2's modern design shows. It supports multiple layout engines, and it has first-class features that older tools lack - SQL-table shapes for entity-relationship diagrams, class shapes for UML-style class boxes, markdown text blocks, and even animated multi-board diagrams that walk through steps. The result is that a single D2 file can produce a polished diagram with little manual tweaking, which is precisely the gap in older diagram-as-code tools that D2 set out to close.
Where D2 fits among the alternatives
D2's sweet spot is architecture and infrastructure diagrams where you want clean output and readable source, and its container model makes system topologies especially pleasant to express. It overlaps with Graphviz, which is more low-level and universal but produces plainer output, and with Mermaid, which is more oriented toward documentation and has the widest built-in support across platforms. Which you choose depends on your priorities: reach and ubiquity point to Mermaid, raw graph power to Graphviz, and modern aesthetics with easy containers to D2.
Whichever text language you settle on, the diagram-as-code discipline is the same - keep the source in version control, review it with the code, and regenerate the image so it never drifts, as the guide at /guides/diagram-as-code-guide describes. Atlas Diagram Studio imports Mermaid directly, so a Mermaid diagram drops straight into the editor at /diagrams; a D2 or DOT diagram serves as a reference you rebuild there when you want visual editing, real-time collaboration, or output beyond what the language produces. To draft any of these from a description, the AI diagram generator at /diagram-tools/ai-diagram-generator gives an editable starting point, and /guides/mermaid-js-complete-guide covers the format Atlas supports natively.