# How to Visualize Knowledge Graphs with React Flow in the Understand-Anything Dashboard

> Visualize interactive knowledge graphs in the Understand Anything dashboard using React Flow. Learn how pipeline output and D3 layouts create dynamic visualizations.

- Repository: [Egonex/Understand-Anything](https://github.com/Egonex-AI/Understand-Anything)
- Tags: how-to-guide
- Published: 2026-06-24

---

**The Understand-Anything dashboard renders interactive knowledge graphs by feeding JSON pipeline output into a React Flow canvas, using D3 force-directed layouts for positioning and a Zustand store for state management.**

The open-source repository [Egonex-AI/Understand-Anything](https://github.com/Egonex-AI/Understand-Anything) provides a React-based dashboard that transforms static knowledge graph data into explorable node-edge diagrams. This implementation combines a centralized state store, deterministic force-simulation layouts, and React Flow’s component library to handle graphs with hundreds of nodes while maintaining 60fps interactions.

## Architecture Overview

The visualization pipeline relies on three coordinated layers. The **Zustand store** ([`store.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/store.ts)) maintains the raw `KnowledgeGraph` object and derived indexes. The **layout engine** ([`utils/layout.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/utils/layout.ts)) computes stable (x, y) coordinates using D3 force simulation. Finally, the **React Flow view** ([`KnowledgeGraphView.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/KnowledgeGraphView.tsx)) maps these positioned entities into interactive React components, handling clicks, search highlighting, and edge styling.

## Loading and Validating the Knowledge Graph

When the dashboard initializes in [`App.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/App.tsx), it fetches the knowledge graph JSON produced by the `/understand-knowledge` pipeline. The data undergoes schema validation before entering the global store.

```tsx
// App.tsx (lines 35-45)
fetch(dataUrl("knowledge-graph.json", accessToken))
  .then(r => r.json())
  .then((data: unknown) => {
    const result = validateGraph(data);
    if (result.success && result.data) {
      setGraph(result.data);  // Injects validated graph into the store
    }
  });

```

*Source:* [[`understand-anything-plugin/packages/dashboard/src/App.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/App.tsx)](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/App.tsx#L35-L45)

## State Management with Zustand

The `useDashboardStore` creates a `SearchEngine` for fuzzy lookup and builds fast-access indexes (`nodesById`, `nodeIdToLayerId`) to avoid O(n) scans during rendering.

```ts
// store.ts (lines 66-74)
const searchEngine = new SearchEngine(graph.nodes);
const { nodesById, nodeIdToLayerId, nodeIdToLayerIds } = buildGraphIndexes(graph);
set({ 
  graph, 
  nodesById, 
  nodeIdToLayerId, 
  nodeIdToLayerIds, 
  searchEngine 
});

```

*Source:* [[`understand-anything-plugin/packages/dashboard/src/store.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/store.ts)](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/store.ts#L66-L74)

## Computing Force-Directed Layouts

The `KnowledgeGraphView` component triggers layout computation via `computeLayout`, which internally calls `applyForceLayout` from [`utils/layout.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/utils/layout.ts). The engine runs a deterministic number of D3 simulation ticks based on graph density to ensure stable positioning without blocking the UI thread.

```ts
// KnowledgeGraphView.tsx (lines 40-44)
const { positionMap, edgeCounts } = useMemo(() => {
  if (!filteredGraph) return { positionMap: new Map(), edgeCounts: new Map() };
  return computeLayout(filteredGraph);
}, [filteredGraph]);

```

The underlying simulation configures link distance, charge strength, and centering forces, then executes `Math.min(300, Math.max(100, nodes.length))` ticks to balance quality and performance.

```ts
// utils/layout.ts (lines 31-70)
const sim = forceSimulation<ForceNode>(simNodes)
  .force("link", forceLink(...).distance(linkDistance))
  .force("charge", forceManyBody().strength(chargeStrength))
  .force("center", forceCenter(0, 0).strength(0.03));

const ticks = Math.min(300, Math.max(100, nodes.length));
sim.tick(ticks);

```

*Source:* [[`understand-anything-plugin/packages/dashboard/src/utils/layout.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/utils/layout.ts)](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/utils/layout.ts#L31-L70)

## Rendering Nodes and Edges in React Flow

With positions calculated, the component maps graph entities to React Flow’s `Node` and `Edge` types. Node data includes UI-specific flags (`isSelected`, `isHighlighted`, `incomingCount`) to enable visual feedback without recomputing layout.

```ts
// KnowledgeGraphView.tsx (lines 58-95)
const rfNodes: Node[] = filteredGraph.nodes.map(node => ({
  id: node.id,
  type: "custom",
  position: positionMap.get(node.id) ?? { x: 0, y: 0 },
  data: {
    label: node.name,
    nodeType: node.type,
    isSelected: selectedNodeId === node.id,
    isHighlighted: highlightedNodeIds.has(node.id),
    incomingCount: edgeCounts.get(node.id) ?? 0,
    onNodeClick: handleNodeClick,
  },
}));

```

Edges receive style presets from the `EDGE_STYLES` record, which maps knowledge relationship types (e.g., `cites`, `related`) to CSS properties like `strokeDasharray` and `opacity`.

The final render wraps the mapped arrays in a `ReactFlow` provider with configured zoom limits and UI controls:

```tsx
// KnowledgeGraphView.tsx (lines 46-58)
<ReactFlow
  nodes={nodes}
  edges={edges}
  nodeTypes={nodeTypes}
  fitView
  minZoom={0.05}
  maxZoom={2}
>
  <Background variant={BackgroundVariant.Dots} />
  <Controls />
  <MiniMap />
</ReactFlow>

```

*Source:* [[`understand-anything-plugin/packages/dashboard/src/components/KnowledgeGraphView.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/components/KnowledgeGraphView.tsx)](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/components/KnowledgeGraphView.tsx#L46-L58)

## Handling User Interactions

User actions mutate the Zustand store rather than local React state, ensuring consistent data across the dashboard. The `selectNode` and `setFocusNode` methods update `selectedNodeId` and `highlightedNodeIds`, triggering React Flow to re-render styles via memoized selector dependencies. This pattern prevents expensive layout recalculation during hover, search, or filter operations.

To highlight a node programmatically:

```ts
import { useDashboardStore } from "./store";

function highlightNode(nodeId: string) {
  const selectNode = useDashboardStore.getState().selectNode;
  selectNode(nodeId);  // Updates selectedNodeId → UI re-renders with highlighted styles
}

```

## Summary

- **Data ingestion**: The dashboard loads JSON knowledge graphs via `fetch` in [`App.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/App.tsx), validating against `@understand-anything/core/schema` before storage.
- **State architecture**: `useDashboardStore` maintains graph data, search indexes, and selection state using Zustand for predictable updates.
- **Positioning**: `applyForceLayout` in [`utils/layout.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/utils/layout.ts) runs D3 force simulation with adaptive tick counts (`100–300`) to generate stable node coordinates.
- **Rendering**: [`KnowledgeGraphView.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/KnowledgeGraphView.tsx) converts positioned nodes into React Flow components, enriching `data` props with UI state and applying `EDGE_STYLES` for relationship visuals.
- **Performance**: Memoized selectors ensure that interactions (search, selection, filtering) update visual styles without recomputing the force-directed layout.

## Frequently Asked Questions

### How does the layout engine handle large knowledge graphs?

The force simulation adapts its iteration count based on node density, running `Math.min(300, Math.max(100, nodes.length))` ticks. This prevents excessive CPU usage on small graphs while providing sufficient convergence for larger datasets. Results are cached in a `positionMap` via `useMemo`, ensuring React Flow only recalculates positions when the filtered graph changes.

### Can I customize the appearance of nodes and edges?

Yes. Node rendering is delegated to the `CustomNode` component referenced in the `nodeTypes` map, allowing you to modify labels, icons, and click handlers. Edge styles are configured via the `EDGE_STYLES` record in [`KnowledgeGraphView.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/KnowledgeGraphView.tsx), where you can define CSS properties like `stroke`, `strokeWidth`, and `strokeDasharray` for each relationship type (e.g., `cites`, `inspired_by`).

### What data format does the dashboard expect for the knowledge graph?

The dashboard expects a JSON object matching the `KnowledgeGraph` schema defined in `@understand-anything/core/schema`. This includes arrays of nodes (with `id`, `name`, and `type` fields) and edges (with `source`, `target`, and relationship type fields). The `validateGraph` function in [`App.tsx`](https://github.com/Egonex-AI/Understand-Anything/blob/main/App.tsx) ensures type safety before the data enters the store.

### How are user interactions like search and selection implemented?

Interactions mutate the global Zustand store (`selectNode`, `setSearchQuery`) rather than local component state. The `KnowledgeGraphView` subscribes to these store slices via selectors, passing derived boolean flags (`isSelected`, `isHighlighted`) into each node’s `data` prop. Because React Flow re-renders nodes when the `nodes` array reference changes, and because the layout computation is memoized, visual updates occur without triggering expensive D3 recalculations.