Skip to main content

DOT (Graphviz) Format

Convert graphs to and from Graphviz DOT format for layout and rendering.

Features

  • Compound graphs: Full support via subgraphs
  • Layout hints: Direction (rankdir), shape, color
  • Both formats: Directed (digraph) and undirected (graph)
  • Parser included: Uses dotparser for parsing

Installation

Requires the dotparser peer dependency:
pnpm add dotparser

Import

import { toDOT, fromDOT, dotConverter } from '@statelyai/graph';
// Or from subpath:
import { toDOT } from '@statelyai/graph/dot';

toDOT()

Converts a graph to DOT format string.
import { createGraph, toDOT } from '@statelyai/graph';

const graph = createGraph({
  id: 'G',
  direction: 'right',
  nodes: [
    { id: 'a', label: 'Start', shape: 'ellipse', color: '#90EE90' },
    { id: 'b', label: 'End', shape: 'rectangle' }
  ],
  edges: [
    { id: 'e0', sourceId: 'a', targetId: 'b', label: 'go', color: 'blue' }
  ]
});

const dot = toDOT(graph);
Result:
digraph G {
  rankdir=LR;
  a [label="Start", shape=ellipse, fillcolor="#90EE90" style=filled];
  b [label="End", shape=box];
  a -> b [label="go", color="blue"];
}

Type Signature

function toDOT(graph: Graph): string

Parameters

graph
Graph
required
The graph to convert to DOT format

Returns

A DOT format string.

Mapping

Graph PropertyDOT SyntaxNotes
graph.iddigraph <id>Graph name
graph.typedigraph / graphDirected vs undirected
graph.directionrankdir=TB/BT/LR/RLLayout direction
node.idNode identifierEscaped if needed
node.labellabel="..."Node label
node.shapeshape=box/ellipse/...See shape mapping below
node.colorfillcolor="..." style=filledNode fill color
node.parentIdSubgraph nestingCompound hierarchy
edge.sourceId -> edge.targetIda -> b or a -- bArrow syntax
edge.labellabel="..."Edge label
edge.colorcolor="..."Edge color

Shape Mapping

Graph ShapeDOT Shape
rectanglebox
ellipseellipse
circlecircle
diamonddiamond
hexagonhexagon
cylindercylinder
parallelogramparallelogram

Direction Mapping

Graph DirectionDOT Rankdir
downTB (top to bottom)
upBT (bottom to top)
rightLR (left to right)
leftRL (right to left)

fromDOT()

Parses a DOT format string into a graph.
import { fromDOT } from '@statelyai/graph';

const graph = fromDOT(`
  digraph {
    a -> b;
    b -> c;
  }
`);

graph.nodes; // [{id: 'a', ...}, {id: 'b', ...}, {id: 'c', ...}]
graph.edges; // [{sourceId: 'a', targetId: 'b', ...}, ...]

Type Signature

function fromDOT(dot: string): Graph

Parameters

dot
string
required
DOT format string to parse

Returns

A Graph object.

Supported DOT Features

Supported:
  • Node and edge declarations
  • Labels, shapes, colors
  • Subgraphs (converted to compound nodes)
  • Graph/node/edge attributes
  • rankdir for direction
  • Both digraph and graph
  • Default attributes (node [shape=box])
  • Attribute statements
⚠️ Limitations:
  • HTML labels stored as-is (not parsed)
  • Port syntax (:port:compass) ignored
  • Layout hints beyond rankdir ignored
  • rank=same and other constraints ignored

Error Handling

try {
  const graph = fromDOT('invalid syntax');
} catch (error) {
  console.error(error.message);
  // "DOT: parse error — unexpected token"
}

fromDOT(''); // Error: DOT: input is empty
fromDOT(123); // Error: DOT: expected a string

dotConverter

Bidirectional converter object.
import { createGraph, dotConverter } from '@statelyai/graph';

const graph = createGraph({
  nodes: [{ id: 'a' }, { id: 'b' }],
  edges: [{ sourceId: 'a', targetId: 'b' }]
});

const dot = dotConverter.to(graph);
const roundTripped = dotConverter.from(dot);

Type

const dotConverter: GraphFormatConverter<string>

Compound Graphs (Subgraphs)

import { createGraph, toDOT } from '@statelyai/graph';

const graph = createGraph({
  nodes: [
    { id: 'cluster_a', label: 'Cluster A' },
    { id: 'a1', parentId: 'cluster_a' },
    { id: 'a2', parentId: 'cluster_a' },
    { id: 'b' }
  ],
  edges: [
    { sourceId: 'a1', targetId: 'a2' },
    { sourceId: 'a2', targetId: 'b' }
  ]
});

const dot = toDOT(graph);
Result:
digraph "" {
  subgraph cluster_a {
    label="Cluster A";
    a1;
    a2;
    a1 -> a2;
  }
  b;
  a2 -> b;
}

Undirected Graphs

import { createGraph, toDOT } from '@statelyai/graph';

const graph = createGraph({
  type: 'undirected',
  nodes: [{ id: 'a' }, { id: 'b' }],
  edges: [{ sourceId: 'a', targetId: 'b' }]
});

const dot = toDOT(graph);
// graph "" {
//   a;
//   b;
//   a -- b;
// }

Rendering with Graphviz

# Save DOT string to file
echo "$DOT_STRING" > graph.dot

# Render as PNG
dot -Tpng graph.dot -o graph.png

# Render as SVG
dot -Tsvg graph.dot -o graph.svg

# Use different layout engines
neato -Tpng graph.dot -o graph.png  # spring layout
fdp -Tpng graph.dot -o graph.png    # force-directed
circo -Tpng graph.dot -o graph.png  # circular

Advanced Example

import { createGraph, toDOT } from '@statelyai/graph';

const graph = createGraph({
  id: 'StateMachine',
  direction: 'right',
  nodes: [
    { id: 'start', label: '', shape: 'circle', color: 'black' },
    { id: 'idle', label: 'Idle', shape: 'rectangle' },
    { id: 'loading', label: 'Loading', shape: 'rectangle' },
    { id: 'success', label: 'Success', shape: 'rectangle', color: '#90EE90' },
    { id: 'error', label: 'Error', shape: 'rectangle', color: '#FFB6C6' }
  ],
  edges: [
    { sourceId: 'start', targetId: 'idle' },
    { sourceId: 'idle', targetId: 'loading', label: 'FETCH' },
    { sourceId: 'loading', targetId: 'success', label: 'RESOLVE' },
    { sourceId: 'loading', targetId: 'error', label: 'REJECT' },
    { sourceId: 'success', targetId: 'idle', label: 'RESET' },
    { sourceId: 'error', targetId: 'idle', label: 'RETRY' }
  ]
});

const dot = toDOT(graph);
console.log(dot);

See Also

Build docs developers (and LLMs) love