react-flow-architecture

existential-birds/beagle · updated Apr 8, 2026

$npx skills add https://github.com/existential-birds/beagle --skill react-flow-architecture
0 commentsdiscussion
summary

Implication: Core logic is framework-agnostic. When contributing or debugging, check if issue is in @xyflow/system or framework-specific package.

skill.md

React Flow Architecture

When to Use React Flow

Good Fit

  • Visual programming interfaces
  • Workflow builders and automation tools
  • Diagram editors (flowcharts, org charts)
  • Data pipeline visualization
  • Mind mapping tools
  • Node-based audio/video editors
  • Decision tree builders
  • State machine designers

Consider Alternatives

  • Simple static diagrams (use SVG or canvas directly)
  • Heavy real-time collaboration (may need custom sync layer)
  • 3D visualizations (use Three.js, react-three-fiber)
  • Graph analysis with 10k+ nodes (use WebGL-based solutions like Sigma.js)

Architecture Patterns

Package Structure (xyflow)

@xyflow/system (vanilla TypeScript)
├── Core algorithms (edge paths, bounds, viewport)
├── xypanzoom (d3-based pan/zoom)
├── xydrag, xyhandle, xyminimap, xyresizer
└── Shared types

@xyflow/react (depends on @xyflow/system)
├── React components and hooks
├── Zustand store for state management
└── Framework-specific integrations

@xyflow/svelte (depends on @xyflow/system)
└── Svelte components and stores

Implication: Core logic is framework-agnostic. When contributing or debugging, check if issue is in @xyflow/system or framework-specific package.

State Management Approaches

1. Local State (Simple Apps)

// useNodesState/useEdgesState for prototyping
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

Pros: Simple, minimal boilerplate Cons: State isolated to component tree

2. External Store (Production)

// Zustand store example
import { create } from 'zustand';

interface FlowStore {
  nodes: Node[];
  edges: Edge[];
  setNodes: (nodes: Node[]) => void;
  onNodesChange: OnNodesChange;
}

const useFlowStore = create<FlowStore>((set, get) => ({
  nodes: initialNodes,
  edges: initialEdges,
  setNodes: (nodes) => set({ nodes }),
  onNodesChange: (changes) => {
    set({ nodes: applyNodeChanges(changes, get().nodes) });
  },
}));

// In component
function Flow() {
  const { nodes, edges, onNodesChange } = useFlowStore();
  return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}

Pros: State accessible anywhere, easier persistence/sync Cons: More setup, need careful selector optimization

3. Redux/Other State Libraries

// Connect via selectors
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();

const onNodesChange = useCallback((changes: NodeChange[]) => {
  dispatch(nodesChanged(changes));
}, [dispatch]);

Data Flow Architecture

User Input → Change Event → Reducer/Handler → State Update → Re-render
[Drag node] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
[Connect]   → onConnect → addEdge → setEdges → ReactFlow
[Delete]    → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow

Sub-Flow Pattern (Nested Nodes)

// Parent node containing child nodes
const nodes = [
  {
    id: 'group-1',
    type: 'group',
    position: { x: 0, y: 0 },
    style: { width: 300, height: 200 },
  },
  {
    id: 'child-1',
    parentId: 'group-1',  // Key: parent reference
    extent: 'parent',      // Key: constrain to parent
    position: { x: 10, y: 30 },  // Relative to parent
    data: { label: 'Child' },
  },
];

Considerations:

  • Use extent: 'parent' to constrain dragging
  • Use expandParent: true to auto-expand parent
  • Parent z-index affects child rendering order

Viewport Persistence

// Save viewport state
const { toObject, setViewport } = useReactFlow();

const handleSave = () => {
  const flow = toObject();
  // flow.nodes, flow.edges, flow.viewport
  localStorage.setItem('flow', JSON.stringify(flow));
};

const handleRestore = () => {
  const flow = JSON.parse(localStorage.getItem('flow'));
  setNodes(flow.nodes);
  setEdges(flow.edges);
  setViewport(flow.viewport);
};

Integration Patterns

With Backend/API

// Load from API
useEffect(() => {
  fetch('/api/flow')
    .then(r => r.json())
    .then(({ nodes, edges }) => {
      setNodes(nodes);
      setEdges(edges);
    });
}, []);

// Debounced auto-save
const debouncedSave = useMemo(
  () => debounce((nodes, edges) => {
    fetch('/api/flow', {
      method: 'POST',
      body: JSON.stringify({ nodes, edges }),
    });
  }, 1000),
  []
);

useEffect(() => {
  debouncedSave(nodes, edges);
}, [nodes, edges]);

With Layout Algorithms

import dagre from 'dagre';

function getLayoutedElements(nodes: Node[], edges: Edge[]) {
  const g = new dagre.graphlib.Graph();
  g.setGraph({ rankdir: 'TB' });
  g.setDefaultEdgeLabel(() => ({}));

  nodes.forEach((node) => {
    g.setNode(node.id, { width: 150, height: 50 });
  });

  edges.forEach((edge) => {
    g.setEdge(edge.source, edge.target);
  });

  dagre.layout(g);

  return {
    nodes: nodes.map((node) => {
      const pos = g.node(node.id);
      return { ...node, position: { x: pos.x, y: pos.y } };
    }),
    edges,
  };
}

Performance Scaling

Node Count Guidelines

Nodes Strategy
< 100 Default settings
100-500 Enable onlyRenderVisibleElements
500-1000 Simplify custom nodes, reduce DOM elements
> 1000 Consider virtualization, WebGL alternatives

Optimization Techniques

<ReactFlow
  // Only render nodes/edges in viewport
  onlyRenderVisibleElements={true}

  // Reduce node border radius (improves intersect calculations)
  nodeExtent={[[-1000, -1000], [1000, 1000]]}

  // Disable features not needed
  elementsSelectable={false}
  panOnDrag={false}
  zoomOnScroll={false}
/>

Trade-offs

Controlled vs Uncontrolled

Controlled Uncontrolled
More boilerplate Less code
Full state control Internal state
Easy persistence Need toObject()
Better for complex apps Good for prototypes

Connection Modes

Strict (default) Loose
Source → Target only Any handle → any handle
Predictable behavior More flexible
Use for data flows Use for diagrams
<ReactFlow connectionMode={ConnectionMode.Loose} />

Edge Rendering

Default edges Custom edges
Fast rendering More control
Limited styling Any SVG/HTML
Simple use cases Complex labels

Discussion

Product Hunt–style comments (not star reviews)
  • No comments yet — start the thread.
general reviews

Ratings

4.539 reviews
  • Aisha Torres· Dec 28, 2024

    react-flow-architecture fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.

  • Olivia Huang· Dec 20, 2024

    I recommend react-flow-architecture for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.

  • Alexander Harris· Dec 20, 2024

    react-flow-architecture reduced setup friction for our internal harness; good balance of opinion and flexibility.

  • Carlos Srinivasan· Dec 16, 2024

    react-flow-architecture has been reliable in day-to-day use. Documentation quality is above average for community skills.

  • Sophia Thompson· Nov 19, 2024

    react-flow-architecture is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.

  • Alexander Zhang· Nov 11, 2024

    Registry listing for react-flow-architecture matched our evaluation — installs cleanly and behaves as described in the markdown.

  • Carlos White· Nov 7, 2024

    Solid pick for teams standardizing on skills: react-flow-architecture is focused, and the summary matches what you get after install.

  • Aisha Shah· Oct 26, 2024

    react-flow-architecture is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.

  • Sofia Khanna· Oct 10, 2024

    Solid pick for teams standardizing on skills: react-flow-architecture is focused, and the summary matches what you get after install.

  • Alexander Smith· Oct 2, 2024

    Useful defaults in react-flow-architecture — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.

showing 1-10 of 39

1 / 4