Documentation

Diagram recipes

Complete starting points for common architecture, flow, lifecycle, lineage, dependency, and topology diagrams.

Pick the layout that matches what the diagram needs to preserve. Layered layout explains relationships. Row, column, and grid explain composition. Manual layout preserves an authored shape.

Service architecture

Use layered layout when relationships should decide placement. Named ports keep the gateway branches attached to stable sides.

service-architecture.tsx
const services = defineDiagram({
  layout: layouts.layered({
    direction: "right",
    spacing: 48,
    layerSpacing: 88
  }),
  nodes: [
    node("client", { label: "Client" }),
    node("gateway", {
      label: "Gateway",
      ports: [
        { id: "sync", side: "right", position: 0.3 },
        { id: "async", side: "right", position: 0.72 }
      ]
    }),
    node("worker", { label: "Worker" }),
    node("queue", { label: "Queue" }),
    node("store", { label: "Store", shape: "ellipse" })
  ],
  edges: [
    edge("request", "client", "gateway"),
    edge("dispatch", anchors.port("gateway", "sync"), "worker"),
    edge("publish", anchors.port("gateway", "async"), "queue"),
    edge("write", "worker", "store"),
    edge("consume", "queue", "store")
  ]
});

<Diagram {...services} spawn={{ mode: "flow", interval: 160 }} />;

Request path

A row layout works for a strict left-to-right sequence. Exact side anchors stop endpoints from moving when node sizes differ.

request-path.tsx
const requestPath = defineDiagram({
  layout: layouts.row({ gap: 72, align: "center" }),
  nodes: [
    node("browser", { label: "Browser" }),
    node("edge", { label: "CDN" }),
    node("api", { label: "API" }),
    node("database", { label: "Database", shape: "ellipse" })
  ],
  edges: [
    edge("tls", anchors.side("browser", "right"), anchors.side("edge", "left"), {
      label: "TLS",
      route: routes.straight(),
      animated: "flow"
    }),
    edge("forward", "edge", "api", { route: routes.straight() }),
    edge("query", "api", "database", { label: "SQL", route: routes.straight() })
  ]
});

Event pipeline

A downward layered graph makes fan-out readable. Automatic routes use ELK bends and still clip to node boundaries.

event-pipeline.tsx
const events = defineDiagram({
  layout: layouts.layered({ direction: "down", edgeRouting: "orthogonal" }),
  nodes: [
    node("producer", { label: "Producer" }),
    node("topic", { label: "Topic", shape: "ellipse" }),
    node("indexer", { label: "Indexer" }),
    node("notifier", { label: "Notifier" }),
    node("archive", { label: "Archive" })
  ],
  edges: [
    edge("publish", "producer", "topic", { animated: "draw" }),
    edge("index", "topic", "indexer", { route: routes.auto(10) }),
    edge("notify", "topic", "notifier", { route: routes.auto(10) }),
    edge("retain", "topic", "archive", { route: routes.auto(10) })
  ]
});

Deployment pipeline

Grid layout fits a known matrix. Use explicit route choices when the diagram should explain each transition rather than mirror graph depth.

deployment-pipeline.tsx
const deploy = defineDiagram({
  layout: layouts.grid({ columns: 2, columnGap: 96, rowGap: 64 }),
  nodes: [
    node("commit", { label: "Commit" }),
    node("build", { label: "Build" }),
    node("test", { label: "Test" }),
    node("release", { label: "Production", shape: "ellipse" })
  ],
  edges: [
    edge("commit-build", "commit", "build", { route: routes.elbow("horizontal", 8) }),
    edge("build-test", "build", "test", { route: routes.elbow("vertical", 8) }),
    edge("test-release", "test", "release", {
      route: routes.orthogonal("horizontal", 0.5, 8)
    })
  ]
});

State machine

Manual layout is the right choice when loops and branch placement carry meaning. Manual waypoints keep the return path outside the main states.

state-machine.tsx
const lifecycle = defineDiagram({
  layout: layouts.manual(),
  nodes: [
    node("draft", { label: "Draft", position: { x: 0, y: 120 } }),
    node("review", { label: "Review", position: { x: 220, y: 120 } }),
    node("live", { label: "Live", position: { x: 440, y: 20 } }),
    node("rejected", { label: "Rejected", position: { x: 440, y: 220 } })
  ],
  edges: [
    edge("submit", "draft", "review", { label: "submit" }),
    edge("approve", "review", "live", { label: "approve" }),
    edge("reject", "review", "rejected", { label: "reject" }),
    edge("revise", "rejected", "draft", {
      route: routes.manual([{ x: 300, y: 330 }, { x: 80, y: 330 }], 12)
    })
  ]
});

Data lineage

Multiple sources can converge on one transform. Edge labels record the actual transfer contract instead of decorating the line.

data-lineage.tsx
const lineage = defineDiagram({
  layout: layouts.layered({ direction: "right" }),
  nodes: [
    node("orders", { label: "Orders DB", shape: "ellipse" }),
    node("events", { label: "Event log", shape: "ellipse" }),
    node("transform", { label: "Transform" }),
    node("warehouse", { label: "Warehouse", shape: "ellipse" }),
    node("dashboard", { label: "Dashboard" })
  ],
  edges: [
    edge("orders-transform", "orders", "transform", { label: "CDC" }),
    edge("events-transform", "events", "transform", { label: "stream" }),
    edge("transform-warehouse", "transform", "warehouse", { label: "merge" }),
    edge("warehouse-dashboard", "warehouse", "dashboard", { label: "query" })
  ]
});

Dependency graph

Layered layout exposes shared dependencies and merge points. The document stays useful outside React because it contains only data.

dependency-graph.ts
const dependencies = defineDiagram({
  layout: layouts.layered({ direction: "down" }),
  nodes: ["app", "editor", "viewer", "document", "utils"].map((id) =>
    node(id, { label: id })
  ),
  edges: [
    edge("app-editor", "app", "editor"),
    edge("app-viewer", "app", "viewer"),
    edge("editor-document", "editor", "document"),
    edge("viewer-document", "viewer", "document"),
    edge("document-utils", "document", "utils")
  ]
});

const result = await computeDiagram(dependencies, { layoutEngine: elkLayoutEngine });

Network topology

Manual positions give a network map a stable physical shape. Side anchors and outward offsets keep lines away from node borders.

network-topology.tsx
const network = defineDiagram({
  layout: layouts.manual(),
  nodes: [
    node("internet", { label: "Internet", shape: "ellipse", position: { x: 220, y: 0 } }),
    node("router", { label: "Router", position: { x: 220, y: 130 } }),
    node("web-a", { label: "Web A", position: { x: 20, y: 280 } }),
    node("web-b", { label: "Web B", position: { x: 220, y: 280 } }),
    node("database", { label: "Database", shape: "ellipse", position: { x: 420, y: 280 } })
  ],
  edges: [
    edge("uplink", anchors.side("internet", "bottom", 0.5, 6), "router"),
    edge("route-a", "router", "web-a", { route: routes.orthogonal("vertical", 0.45, 8) }),
    edge("route-b", "router", "web-b", { route: routes.straight() }),
    edge("route-db", "router", "database", { route: routes.orthogonal("vertical", 0.45, 8) })
  ]
});