DAG Renderer

A directed acyclic graph. Pass a flat list of nodes and directed edges and the component lays them out by topological rank (Kahn's algorithm). Every node at depth k shares the same coordinate on the layout axis, so dependencies read cleanly left-to-right (default) or top-to-bottom. Ideal for narrating topological sort, dependency graphs, computation graphs, and build pipelines.

installlinttestbuilddeploy
Customize
Layout
Highlight
build

Installation

npx shadcn@latest add https://craftbits.dev/r/dag-renderer.json

Usage

import { DAGRenderer, type DAGEdge, type DAGNode } from "@craft-bits/core";
 
const nodes: DAGNode[] = [
  { id: "install", label: "install" },
  { id: "lint",    label: "lint" },
  { id: "test",    label: "test" },
  { id: "build",   label: "build" },
  { id: "deploy",  label: "deploy" },
];
 
const edges: DAGEdge[] = [
  { from: "install", to: "lint" },
  { from: "install", to: "test" },
  { from: "lint",    to: "build" },
  { from: "test",    to: "build" },
  { from: "build",   to: "deploy" },
];
 
<DAGRenderer
  nodes={nodes}
  edges={edges}
  nodeStyles={{ build: "highlight" }}
/>

Top-down direction for tall stack-like reveals:

<DAGRenderer nodes={nodes} edges={edges} direction="top-down" />

Understanding the component

  1. Topological rank via Kahn's algorithm. Each render computes one rank per node by repeatedly peeling off zero-indegree nodes. The rank is the longest path from any source — a node with parents at ranks 1 and 3 lands at rank 4, not rank 2 — so edges flow forward by construction.
  2. Cycles throw. A DAG must be acyclic by contract. If Kahn's pass leaves nodes unresolved, the component throws with the unresolved ids in the message so the caller can pinpoint the offending edges.
  3. Auto-layout, two axes. Nodes at the same rank share the main-axis coordinate (x for top-down, y for left-right) and spread along the cross axis. Within a rank, nodes are sorted by their position in the nodes array so the layout is deterministic across re-renders.
  4. Curved edges for rank skips. In left-right mode, edges that span more than one rank draw as quadratic Bézier arcs via curvedEdgePath. Adjacent-rank edges stay straight. top-down mode draws all edges straight.
  5. Stagger by rank. Node and edge entry animations stagger by topological rank (delay = rank * STAGGER). The graph reveals layer by layer, mirroring the algorithm's frontier expansion.
  6. Four node styles + shared markers. "default" / "highlight" / "visited" / "dimmed" map onto the canonical algorithmic states. Arrowheads come from the shared <SvgDefs> block, so geometry stays consistent across every graph component in the library.
  7. Reduced motion. usePrefersReducedMotion() short-circuits every transition to { duration: 0 } — nodes and edges snap into place when the user has opted out.

Props

PropTypeDefaultDescription
nodesreadonly { id: string; label: string }[]requiredNodes in the graph. Order seeds within-rank slot order.
edgesreadonly { from: string; to: string }[]requiredDirected edges. Must form an acyclic graph.
nodeStylesRecord<string, "default" | "highlight" | "visited" | "dimmed">Per-node visual override keyed by node id.
direction"top-down" | "left-right""left-right"Layout axis.
compactbooleanfalseSqueeze padding ~20%.
classNamestringMerged onto the outer <svg>.

Accessibility

  • The outer <svg> is role="img" with an aria-label summarizing the graph ("Directed acyclic graph with 5 nodes: install, lint, test, build, deploy.").
  • Every node renders its label as a child <text>, so the four node styles (highlight, visited, dimmed, default) layer on top of readable text — color is never the only signal.
  • Motion respects prefers-reduced-motion: node entries, edge draws, and the rank-stagger reveal all collapse to instant when the user has opted out.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/viz/DAGRenderer.tsx). The original took pre-positioned { x, y } node coordinates plus a hex accent and a five-state set (in-degree badges, ready/processed/spotlight sets, highlightEdges). The library version inverts the contract — callers describe the graph, the component computes the layout — and consolidates the state palette to four canonical styles.