Egonex-AI Dashboard Architecture: How React Flow and Zustand Power the Knowledge Graph Visualization

The Egonex-AI dashboard combines React Flow for the interactive canvas and Zustand for centralized state management, implementing a two-stage ELK layout system with dual navigation modes (overview and layer-detail) to render complex knowledge graphs performantly.

The Understand-Anything repository implements a graph-first dashboard that visualizes codebases as interactive knowledge graphs. Built on top of @xyflow/react (React Flow) and zustand, the architecture cleanly separates visual rendering from state logic, enabling sophisticated graph layouts computed off the main thread. This deep dive examines the implementation in src/store.ts and src/components/GraphView.tsx, revealing how the system coordinates two navigation levels while maintaining reactive synchronization between the layout engine and the UI.

React Flow: The Interactive Canvas Layer

React Flow provides the visual foundation for the dashboard, handling panning, zooming, mini-maps, and node interactions. The integration centers on src/components/GraphView.tsx, which registers custom node components and manages the React Flow provider context.

Custom Node Type Registration

The dashboard defines four distinct visual representations for graph entities. In GraphView.tsx (lines 57–62), the nodeTypes object maps type strings to React components:

// src/components/GraphView.tsx
const nodeTypes = {
  custom: CustomNode,
  "layer-cluster": LayerClusterNode,
  portal: PortalNode,
  container: ContainerNode,
};

Each component receives the node’s data payload and can access the Zustand store to render contextual information. When the topology builders (useOverviewGraph or useLayerDetailTopology) generate node arrays, they specify these type keys to determine rendering behavior.

Dual Navigation Topologies

The dashboard implements two distinct graph views: overview (showing clustered layers) and layer-detail (showing expanded file-level topology). The GraphView.tsx component switches between these modes based on the navigationLevel and activeLayerId properties from the store.

The useOverviewGraph hook generates aggregated nodes and edges representing high-level architecture, while useLayerDetailTopology constructs the expanded view with container nodes and file-level edges. Both hooks return Node and Edge arrays that feed directly into React Flow’s useNodesState and useEdgesState hooks.

Fit-View Coordination

Two dedicated components handle viewport synchronization: TourFitView and SelectedNodeFitView (lines 95–124). These watch the Zustand store for tourHighlightedNodeIds or selectedNodeId changes, then call React Flow’s fitView method with getInternalNode to ensure highlighted nodes remain centered. The fit-view logic polls until all target nodes are rendered, providing smooth animated transitions during tour navigation or search selection.

Zustand: The Single Source of Truth

The global store defined in src/store.ts holds the entire application state, from the raw knowledge graph to UI flags and layout caches. Accessed via useDashboardStore, it enables selectors that prevent unnecessary re-renders while maintaining reactive updates across the component tree.

Core State Properties

The store maintains several critical data structures:

  • graph, nodesById, nodeIdToLayerId: The raw knowledge graph and O(1) lookup maps built once setGraph initializes the data (via buildGraphIndexes). GraphView reads these via selectors like useDashboardStore((s) => s.graph).

  • navigationLevel, activeLayerId: Enums tracking whether the UI shows the overview or layer-detail view and which layer is currently active. These determine which topology hook supplies nodes to React Flow.

  • expandedContainers: A Set containing IDs of container nodes the user has expanded. When populated, it triggers Stage 2 layout calculations for those specific containers.

  • containerLayoutCache: A Map storing computed ELK layouts for expanded containers, preventing redundant calculations when users toggle containers open and closed.

  • nodeTypeFilters: A record mapping node categories (code, config, docs, etc.) to boolean visibility states, driving the NODE_TYPE_TO_CATEGORY filtering logic (lines 70–78 of GraphView.tsx).

  • tourHighlightedNodeIds, tourFitPending: Tour navigation state that coordinates with TourFitView to animate the viewport to specific nodes during guided tours.

Actions and Cache Coherence

The store exposes action methods like selectNode, drillIntoLayer, toggleContainer, and bumpStage1Tick. These mutate state while maintaining derived cache consistency. For instance, toggling a container clears the containerLayoutCache for that ID (lines 27–40 of store.ts), and changing filters or navigation levels resets the Stage 1 layout tick to trigger a full re-layout.

Two-Stage ELK Layout Architecture

To prevent UI blocking on large graphs, the dashboard implements a two-stage layout strategy using the Eclipse Layout Kernel (ELK), orchestrated through src/utils/elk-layout.ts.

Stage 1: Global Layer Layout

The first stage runs on the layer level, positioning clusters, portals, and containers. It receives nodes with estimated dimensions (containerWidth, containerHeight) and produces a global coordinate system via applyElkLayout. The results merge back into React Flow’s node state through mergeElkPositions. This stage executes whenever stage1Tick changes in the store, which occurs after initial graph load or when filters invalidate the current layout.

Stage 2: Lazy Container Layout

When users expand a container node, the system triggers Stage 2, which computes detailed layouts for that container’s children only. These results are cached in containerLayoutCache to optimize repeated toggling. If the actual rendered size deviates more than 20% from the Stage 1 estimate, the system sets a deviated flag (around lines 664–666) that triggers a Stage 1 re-run to prevent layout drift, while avoiding infinite re-layout loops through size threshold checks.

Reactive Layout Dependencies

Both stages are entirely reactive, depending on store selectors like built, stage1Tick, and expandedContainers. The layout hooks recompute automatically when underlying data changes, ensuring the visualization remains synchronized with the application state without manual coordination.

Wiring the Architecture Together

src/App.tsx mounts the ReactFlowProvider at the root, wrapping the GraphViewInner component. Inside, store selectors determine which topology to render, while useEffect callbacks synchronize React Flow’s internal state with the Zustand store. Graph transformations—including container derivation in src/utils/edgeAggregation.ts and ELK computation—remain pure functions under src/utils/, keeping the UI layer thin and declarative.

Code Examples

Registering a Custom Node Type

Extend the graph visualization by adding a new node type to the registry:

// src/components/GraphView.tsx
import MySpecialNode from "./MySpecialNode";

const nodeTypes = {
  custom: CustomNode,
  "layer-cluster": LayerClusterNode,
  portal: PortalNode,
  container: ContainerNode,
  "my-special": MySpecialNode,   // Register new type
};

Nodes emitted from useLayerDetailTopology with type: "my-special" will now render using this component.

Toggling Container State from UI

Access the store to expand or collapse container nodes:

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

function ExpandButton({ id }: { id: string }) {
  const toggle = useDashboardStore((s) => s.toggleContainer);
  return (
    <button onClick={() => toggle(id)}>
      Toggle {id}
    </button>
  );
}

This updates expandedContainers and automatically invalidates the relevant cache entries in containerLayoutCache.

Synchronizing Fit View with Selection

Integrate search results with the viewport controller:

import { useDashboardStore } from "../store";
import { useReactFlow } from "@xyflow/react";

function SearchResult({ nodeId }: { nodeId: string }) {
  const { fitView } = useReactFlow();
  const select = useDashboardStore((s) => s.selectNode);

  const handleClick = () => {
    select(nodeId);
    fitView({
      nodes: [{ id: nodeId }],
      duration: 500,
      padding: 0.3,
    });
  };

  return <div onClick={handleClick}>Focus {nodeId}</div>;
}

The SelectedNodeFitView component (lines 81–106) implements similar logic automatically when selectedNodeId changes, ensuring the canvas centers on selected nodes.

Summary

  • React Flow handles the interactive canvas in src/components/GraphView.tsx, registering custom node types like LayerClusterNode and ContainerNode while providing fitView capabilities through helper components.
  • Zustand centralizes state in src/store.ts, tracking graph data, navigation levels (navigationLevel, activeLayerId), expanded containers, and layout caches with reactive selectors.
  • Two-stage layout uses ELK to compute layer-level positions (Stage 1) and lazy container details (Stage 2), caching results in containerLayoutCache to prevent redundant calculations.
  • Reactive coordination ensures that state changes automatically trigger layout recomputation and viewport adjustments without imperative manual synchronization.

Frequently Asked Questions

How does the dashboard prevent UI blocking when laying out large graphs?

The architecture performs heavy layout computations using the ELK (Eclipse Layout Kernel) engine in src/utils/elk-layout.ts through a two-stage process. Stage 1 handles the global layer layout, while Stage 2 lazily computes detailed layouts only for expanded containers. These calculations run off the main thread where possible, and results are cached in containerLayoutCache to avoid recalculating positions when users toggle containers.

What triggers a re-layout of the graph visualization?

Three primary conditions trigger re-layout: changes to nodeTypeFilters or navigation levels (which reset the store’s stage1Tick), expanding a container node (triggering Stage 2 layout for that specific container), and size deviations greater than 20% between estimated and actual container dimensions (which sets the deviated flag to re-run Stage 1). All triggers are reactive and managed through Zustand store updates.

Why does the architecture use Zustand instead of React Context for state management?

Zustand provides fine-grained selector-based subscriptions that prevent unnecessary re-renders across deep component trees, which is critical when managing large graph datasets and frequent viewport updates. The store also handles complex derived state like containerLayoutCache and nodesById lookup maps that would require significant boilerplate with Context, while maintaining type safety and performance for real-time graph interactions.

How do the overview and detail views share the same React Flow instance?

Both views use the same GraphView.tsx component but switch data sources based on the navigationLevel and activeLayerId store properties. The component calls either useOverviewGraph or useLayerDetailTopology to generate nodes and edges, then passes these to React Flow’s useNodesState and useEdgesState hooks. This unified approach allows the ReactFlowProvider to maintain consistent zoom and pan states during transitions between navigation levels.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →