If you need to visualize how a system moves between different states like an order going from "placed" to "shipped" to "delivered" a state machine diagram is one of the clearest ways to do it. Graphviz and its DOT language let you build these diagrams from plain text, which means you can version-control them, automate them, and generate them without any drag-and-drop tools. This article walks you through exactly how to create state machine diagrams in Graphviz DOT language, from basic syntax to real examples you can adapt.
What is a state machine diagram, and why use DOT to draw one?
A state machine diagram (also called a state diagram or state transition diagram) shows the possible states of a system or object and the transitions between them. Each state is a node, and each transition is a labeled arrow connecting two nodes.
Graphviz is an open-source graph visualization tool developed by AT&T Labs. The DOT language is its text-based format for describing graphs. You write a short block of text, run it through Graphviz, and get a rendered diagram as SVG, PNG, or PDF. This approach is popular because diagrams live as code easy to store, diff, and regenerate.
If you're new to the syntax itself, our beginner syntax reference for DOT language covers the fundamentals you'll need before diving in here.
How does a basic state machine diagram look in DOT?
The simplest state machine has a few states and transitions between them. Here's a traffic light example:
digraph traffic_light {
rankdir=LR;
node [shape=circle];
Green -> Yellow [label="timer"];
Yellow -> Red [label="timer"];
Red -> Green [label="timer"];
}
Let's break this down:
digraphdeclares a directed graph (arrows have direction, which is what state machines need).rankdir=LRmakes the diagram flow left to right instead of top to bottom.node [shape=circle]sets every node to a circle shape, which is the standard look for state diagrams.- Each
->line defines a transition from one state to another, with a label explaining what triggers it.
How do I represent the start and end states?
State machine diagrams conventionally use a filled black circle for the initial state and a double-bordered circle (or a circle within a circle) for final states. You can set these up with specific node attributes:
digraph order_process {
rankdir=LR;
node [shape=circle];
start [shape=point, label=""];
end [shape=doublecircle, label=""];
start -> Placed;
Placed -> Processing [label="payment confirmed"];
Processing -> Shipped [label="dispatched"];
Shipped -> Delivered [label="received"];
Delivered -> end;
}
Here, shape=point gives you that small filled dot for the initial state, and shape=doublecircle gives you the standard final-state look.
How do I add guard conditions and event labels to transitions?
In real-world state machines, transitions usually have both an event (what happens) and sometimes a guard condition (a requirement that must be true). You can show both on a transition label:
Locked -> Unlocked [label="key / unlock()"];
Unlocked -> Locked [label="lock / lock()"];
The convention is event [guard] / action, but you can adapt the label format to whatever notation your team uses. The important thing is that the DOT label string is just text you can write whatever makes sense for your domain.
How do I group states or show substates?
Sometimes you need to show that a state contains substates. For example, a "Processing" state might internally have "Validating," "Charging," and "Fulfilling" steps. DOT doesn't have native support for state nesting the way UML tools do, but you can use subgraphs to simulate it:
subgraph cluster_Processing {
label="Processing";
style=dashed;
Validating -> Charging [label="valid"];
Charging -> Fulfilling [label="paid"];
}
The cluster_ prefix tells Graphviz to draw a bounding box around those nodes. The box gets labeled with whatever you set as the label for the subgraph. It's not a perfect UML representation, but it communicates the hierarchy clearly.
What styling options make state diagrams easier to read?
A few styling tweaks go a long way toward readability:
- Use
rankdir=LRfor linear flows. Use the default (top to bottom) for branching workflows. - Set a consistent node shape with
node [shape=circle]ornode [shape=box, style=rounded]if you prefer rounded rectangles. - Color-code transitions for clarity:
Green -> Yellow [label="timer", color=orange]; - Use
fontsizeon labels if they're getting cut off or too small. - Avoid crossing arrows by reordering your node definitions or using
constraintattributes to control layout.
What are common mistakes when building state diagrams in DOT?
- Forgetting the semicolons. Every statement in DOT needs a semicolon. Missing one causes cryptic parse errors.
- Using
graphinstead ofdigraph. State machines need directed edges (->). If you usegraph, you'll get undirected edges (--) which don't show transition direction. - Overcrowding the diagram. If you have more than 15–20 states, consider splitting into multiple diagrams or using subgraphs to organize the layout.
- Inconsistent naming. DOT treats
NodeAandnodeaas different node IDs. Pick a naming convention and stick with it. - Ignoring label readability. Long transition labels can overlap. Use
\nto break labels across lines, or shorten the text.
When would I choose DOT over a visual diagram tool?
DOT works best when your diagrams need to live alongside code in a Git repo, in documentation-as-code pipelines, or in automated build processes. You can also generate state diagrams programmatically by writing scripts that output DOT text, which is useful when diagrams depend on data or configuration.
Compared to Mermaid (another text-based diagramming syntax), Graphviz gives you more layout control and handles larger, more complex graphs better. If you're deciding between the two for system architecture work, our comparison of Graphviz DOT and Mermaid for system architecture diagrams covers the tradeoffs in detail.
For a full overview of everything DOT can do beyond state diagrams, see our complete guide to creating diagrams in Graphviz DOT language.
How do I render my DOT file into an image?
Once you've written your DOT file, you need a tool to render it. The most common options:
- Command line:
dot -Tpng traffic_light.dot -o traffic_light.png(the-Tflag sets the output format; you can usesvg,pdf, orpng). - Online editors: Graphviz Online lets you paste DOT code and preview it instantly in your browser.
- VS Code extensions: The "Graphviz Preview" extension renders DOT files directly in your editor.
- Python: The
graphvizPython package lets you build and render diagrams from code.
Quick checklist for creating your first state machine diagram in DOT
- Start with
digraphand open curly braces. - Set
rankdirand defaultnode [shape=circle]. - Define your initial state with
shape=pointand final state withshape=doublecircle. - List every transition as
StateA -> StateB [label="event"];with a semicolon. - Add
subgraph cluster_blocks if you need grouped or nested states. - Render with
dot -Tsvg yourfile.dot -o output.svgand check readability. - Iterate on layout reorder statements, add
rankconstraints, or adjust shapes until it reads clearly.
Beginner's Guide to Graphviz Dot Language Syntax
Graphviz Dot Online Editor with Live Preview and Export Tools
Graphviz Dot Language vs Mermaid for System Architecture Diagrams
Graphviz Dot Language Advanced Node Styling and Ranking Attributes
E-Commerce Website Database Schema Diagram Example with Tables and Relationships
Online Database Schema Diagram and Code Generator Tool