Files

15 KiB

React Flow Documentation Visualizations

Use React Flow when structured documentation data needs an interactive spatial view: roadmaps, Gantt-like timelines, dependency graphs, or traceability maps between Features, requirements, and architectural decisions. React Flow is a renderer and interaction layer, not the authoritative data store and not a built-in layout engine.

This capability assumes the local YAML data pattern in local-yaml-data-database.md. Build nodes and edges from validated source YAML records. Never maintain a second copy of roadmap or relationship data inside TSX or MDX.

Appropriate uses

Use React Flow for:

  • a roadmap organized by Epic, lane, status, release, or date;
  • a Gantt-like roadmap where time determines horizontal position and lane determines vertical position;
  • Feature-to-Feature dependencies;
  • Requirement-to-Feature n:m traceability;
  • architectural decision-to-Feature n:m traceability;
  • a combined, filterable delivery or architecture dependency map.

Do not use React Flow for ordinary prose, small static tables, or a linear list with no useful spatial relationship. Keep a semantic table fallback for every visualization so the information remains searchable, printable, and accessible without client-side JavaScript.

Official baseline

Use the current package name documented by React Flow:

pnpm add @xyflow/react

Import the required base stylesheet once in the visualization component or shared theme entry:

import '@xyflow/react/dist/style.css';

Relevant official guidance:

React Flow does not provide one automatic graph layout. Its official layouting guide describes external options including Dagre, D3, and ELK. Choose and document the layout deliberately.

Component boundary

Keep data loading, graph adaptation, layout, and rendering separate:

data/**/*.yml
    ↓ build-time validation
DocumentationDatabase
    ↓ pure adapter
GraphModel { nodes, edges, fallbackRows, legend }
    ↓ pure layout
PositionedGraphModel
    ↓ React component
RoadmapFlow | DependencyMap
    ↓ MDX composition
Documentation page

Recommended source structure:

src/components/documentation-data/
├── react-flow/
│   ├── DocumentationFlow.tsx
│   ├── RoadmapFlow.tsx
│   ├── DependencyMap.tsx
│   ├── nodes/
│   │   ├── FeatureNode.tsx
│   │   ├── RequirementNode.tsx
│   │   └── ArchitectureDecisionNode.tsx
│   ├── adapters/
│   │   ├── roadmapGraph.ts
│   │   ├── featureDependencyGraph.ts
│   │   ├── requirementFeatureGraph.ts
│   │   └── decisionFeatureGraph.ts
│   ├── layout/
│   │   ├── ganttLayout.ts
│   │   └── dependencyLayout.ts
│   └── DocumentationFlow.module.css
└── fallbacks/
    ├── RoadmapTable.tsx
    └── TraceabilityTable.tsx

Adapters and layout functions must be pure TypeScript functions. Test them without a browser. React components receive already validated records or a typed graph model; they do not parse files.

Docusaurus client boundary

React Flow 12 supports server rendering when node dimensions and handle positions are supplied. That is an advanced path. For normal interactive Docusaurus documentation, use Docusaurus BrowserOnly to avoid hydration differences and browser-global failures:

import BrowserOnly from '@docusaurus/BrowserOnly';
import type {Props} from './DependencyMap';

export default function DependencyMapClient(props: Props) {
  return (
    <BrowserOnly fallback={<div>Loading dependency map…</div>}>
      {() => {
        const DependencyMap = require('./DependencyMap').default;
        return <DependencyMap {...props} />;
      }}
    </BrowserOnly>
  );
}

Use @docusaurus/BrowserOnly, not an unguarded window check scattered through components. If server-rendered graph HTML is an explicit requirement, follow the official React Flow 12 SSR guidance and provide deterministic node width and height, handle positions, and initial viewport data.

Required canvas sizing

React Flow requires a parent with explicit dimensions. Never rely on content height:

.canvas {
  width: 100%;
  height: min(72vh, 760px);
  min-height: 420px;
  border: 1px solid var(--ifm-color-emphasis-300);
  border-radius: 0;
}

:global(.react-flow__node),
:global(.react-flow__controls-button),
:global(.react-flow__minimap) {
  border-radius: 0;
}

Use sharp edges throughout. Do not introduce pill nodes, rounded cards, rounded controls, or rounded filter inputs.

Shared read-only renderer

Documentation views are read-only unless an editing workflow is separately approved. Disable accidental graph mutation:

import {
  Background,
  Controls,
  MiniMap,
  ReactFlow,
  ReactFlowProvider,
  type Edge,
  type Node,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';

export function DocumentationFlow({nodes, edges}: {
  nodes: Node[];
  edges: Edge[];
}) {
  return (
    <div className={styles.canvas} aria-label="Documentation dependency map">
      <ReactFlowProvider>
        <ReactFlow
          nodes={nodes}
          edges={edges}
          fitView
          nodesDraggable={false}
          nodesConnectable={false}
          deleteKeyCode={null}
          nodesFocusable
          edgesFocusable
          disableKeyboardA11y={false}
          minZoom={0.35}
          maxZoom={1.8}
          proOptions={{hideAttribution: false}}
        >
          <Controls showInteractive={false} />
          <MiniMap pannable zoomable />
          <Background />
        </ReactFlow>
      </ReactFlowProvider>
    </div>
  );
}

Keep attribution behavior compliant with the installed package's license and React Flow terms. Do not hide attribution merely for visual preference.

Roadmap and Gantt-like layout

React Flow is not a dedicated Gantt package. Implement a Gantt-like roadmap as a deterministic node layout over validated scheduling fields.

Recommended Feature source fields:

id: feature-001
title: Display project lifecycle state
epic_id: epic-001
status: in-progress
depends_on_feature_ids:
  - feature-000
roadmap:
  start: 2026-08-01
  end: 2026-08-21
  lane: portal
  progress: 60

Layout rules:

  1. Parse dates in one declared timezone and reject invalid ranges.
  2. Compute x from start relative to the roadmap's minimum date.
  3. Compute node width from the duration between start and end.
  4. Compute y from the stable lane order in data or configuration.
  5. Draw Feature dependency edges from depends_on_feature_ids.
  6. Render release or milestone markers as non-connectable custom nodes.
  7. Preserve a consistent time scale while zooming; include a visible date-axis component outside the graph viewport.
  8. Provide filters for Epic, status, lane, release, and owner without mutating source data.
  9. Provide a table fallback ordered by lane, start date, end date, and stable Feature ID.

Do not let dragging persist a new schedule. A documentation reader moving a node is not a data edit. If manual positioning is required for a non-time-based roadmap, store approved coordinates in source YAML and validate them.

For a roadmap without dates, use a phase or status-column layout. Keep phases in source data and calculate positions deterministically. Do not infer ordering from file enumeration.

Dependency layouts

Feature dependencies

Build one node per Feature and one directed edge per depends_on_feature_ids entry. Reject missing Feature IDs. Detect cycles and either fail validation for a declared DAG or render cycles with an explicit warning when cyclic relationships are valid domain data.

For ordinary directed acyclic Feature graphs, use Dagre:

pnpm add @dagrejs/dagre

Dagre is appropriate for a compact, deterministic directed layout. Set node dimensions before layout and map Dagre's center-based coordinates to React Flow positions.

Complex or grouped dependencies

For larger maps with nested Epics, compound nodes, multiple ports, or stronger edge-routing requirements, use ELK:

pnpm add elkjs

ELK layout is asynchronous. Resolve it before rendering the final graph and show a stable loading state. Do not run a new layout on every React render.

Requirement-to-Feature traceability

Create typed nodes and edges from canonical requirement feature_ids:

Functional Requirement ── satisfies/constrains ──> Feature
Non-functional Requirement ── constrains ──> Feature

Use distinct node types, edge styles, labels, and a visible legend. Filters must distinguish functional and non-functional requirements. Do not encode meaning only with color.

Architectural decision-to-Feature traceability

Create typed nodes and edges from canonical architectural decision feature_ids:

Architectural Decision ── governs/affects ──> Feature

Display decision status and link each node to its canonical documentation route. A superseded decision remains traceable but must be visually and textually identified as superseded.

Combined maps

A combined map may include Features, requirements, and architectural decisions, but it must start with a constrained default scope. Large all-record graphs become unreadable and expensive. Require one or more of:

  • selected Epic;
  • selected Feature;
  • selected requirement type;
  • selected decision status;
  • bounded dependency depth;
  • search result or explicit ID list.

Apply filtering before layout so hidden nodes do not consume space.

Adapter contract

A graph adapter should return all rendering and fallback information from one source:

export type DocumentationGraph = {
  nodes: Node<DocumentationNodeData>[];
  edges: Edge<DocumentationEdgeData>[];
  fallbackRows: TraceabilityRow[];
  legend: LegendItem[];
  warnings: GraphWarning[];
};

export function requirementFeatureGraph(
  database: DocumentationDatabase,
  filter: RequirementFeatureFilter,
): DocumentationGraph;

Requirements for every adapter:

  • stable node IDs equal stable database IDs;
  • stable edge IDs derived from source type, source ID, relation, and target ID;
  • no random coordinates or IDs;
  • no filesystem access;
  • no mutation of database records;
  • explicit behavior for missing, filtered, superseded, or cyclic records;
  • table fallback rows derived in the same function as nodes and edges.

MDX composition

MDX chooses the view and scope. It does not define nodes or edges:

---
title: "Explore Feature dependencies"
description: "Inspect Feature dependencies and related requirements."
---

import DependencyMapClient from '@site/src/components/documentation-data/react-flow/DependencyMapClient';
import TraceabilityTable from '@site/src/components/documentation-data/fallbacks/TraceabilityTable';

<DependencyMapClient
  mode="feature-requirements"
  epicId="epic-001"
/>

<TraceabilityTable
  mode="feature-requirements"
  epicId="epic-001"
/>

The map and table must consume the same adapter output or the same normalized selector so they cannot disagree.

Accessibility

React Flow provides keyboard and screen-reader support. Preserve it:

  • keep nodesFocusable and edgesFocusable enabled;
  • keep disableKeyboardA11y={false};
  • provide meaningful ariaLabel values for custom nodes and edges;
  • ensure custom node links and controls have visible focus states;
  • provide text labels in addition to colors and line styles;
  • provide a legend that explains node types, statuses, and edge meanings;
  • provide an adjacent semantic table fallback with the same filtered records;
  • preserve keyboard access to filters, fit-view controls, and linked detail pages;
  • test at 200% zoom and in both light and dark themes.

Read-only means mutation is disabled, not navigation or focus.

Performance

  • Memoize node types outside React components.
  • Build graph data with pure selectors and memoize by database revision and filter state.
  • Filter before layout.
  • Avoid recreating nodes and edges on every render.
  • Do not render the full database by default.
  • Use bounded dependency depth for large graphs.
  • Lazy-load the BrowserOnly visualization component.
  • Run asynchronous ELK layout only when the relevant graph input changes.
  • Measure browser interaction with representative production-size data, not only two-node examples.

Testing and verification

Test pure transformation and layout behavior first:

  • scheduling dates produce stable Gantt x, width, and lane y values;
  • Feature dependencies produce the expected directed edges;
  • Requirement and architectural decision n:m mappings produce complete edges;
  • filters remove nodes before layout;
  • dangling IDs fail validation before graph construction;
  • stable inputs produce byte-equivalent graph IDs and coordinates;
  • fallback table rows represent the same relationships as graph edges;
  • cycles follow the declared policy.

Then test React behavior:

  • the BrowserOnly fallback renders during static generation;
  • the canvas mounts with explicit width and height;
  • nodes and edges are keyboard focusable;
  • graph mutation is disabled;
  • filters, detail links, fit view, pan, and zoom work;
  • missing and empty datasets render useful states;
  • table fallback remains usable without the graph.

Required delivery checks:

pnpm data:validate
pnpm data:check
pnpm test
pnpm typecheck
pnpm build

After the production build, exercise each affected page in a real browser. Verify light and dark themes, desktop and narrow widths, keyboard navigation, labels, filters, links, pan/zoom, empty states, and the table fallback. Then verify the exact Gitea Actions SHA and deployed route.

Acceptance checklist

  • @xyflow/react and its base stylesheet are installed and imported.
  • React Flow receives validated build-time data from source YAML through a pure adapter.
  • Roadmap scheduling and Feature dependencies are stored in YAML, not TSX or MDX.
  • Gantt-like positions are deterministic from dates and lanes.
  • Dagre or ELK is chosen explicitly for dependency layout; React Flow is not described as providing automatic layout.
  • Feature, requirement, and architectural decision relationships use stable IDs and typed nodes/edges.
  • Docusaurus uses BrowserOnly, or the advanced React Flow 12 SSR dimensions are fully configured.
  • The canvas has explicit width and height.
  • Documentation views disable dragging, connecting, and deletion.
  • Keyboard and screen-reader support remains enabled.
  • Sharp styling applies border-radius: 0 to nodes, controls, minimaps, filters, and surrounding panels.
  • Every visualization has a semantic table fallback derived from the same adapter.
  • Unit, component, type, production build, browser, Actions, and deployed-route checks pass.