React Flow

existential-birds/beagle/plugins/beagle-react/skills/react-flow

by existential-birdsd1a74899fbfec74974d1818e4cac7c3d54d44b65No license83 starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 3 weeks ago

React Flow (@xyflow/react) for workflow visualization with custom nodes and edges. Use when building graph visualizations, creating custom workflow nodes, implementing edge labels, or controlling viewport. Triggers on ReactFlow, @xyflow/react, Handle, NodeProps, EdgeProps, useReactFlow, fitView.

AI-generated overview

Guides building node-based graph and workflow visualizations with the React Flow (@xyflow/react) library.

What it does
This skill provides reference instructions for using React Flow (@xyflow/react) to build node-based graphs, workflow editors and interactive diagrams. It covers installation, basic setup, nodes, edges, handles, state management, custom nodes and edges, viewport control, events, common patterns and a provider pattern, with code examples throughout. It also lists sequenced implementation checks for CSS bundling, stable nodeTypes/edgeTypes and provider boundaries, and points to four reference files for deeper patterns.
When to use it
Use it when building graph visualizations or workflow editors in React, creating custom workflow nodes or edges, implementing edge labels, or controlling the viewport. It is triggered by terms such as ReactFlow, @xyflow/react, Handle, NodeProps, EdgeProps, useReactFlow and fitView.
Requirements
Requires a React project with the @xyflow/react package installed (for example via pnpm add @xyflow/react) and its stylesheet imported. Ships no scripts; it is instructions plus four model-readable reference files.

React Flow

React Flow (@xyflow/react) is a library for building node-based graphs, workflow editors, and interactive diagrams. It provides a highly customizable framework for creating visual programming interfaces, process flows, and network visualizations.

Quick Start

Installation

bash
pnpm add @xyflow/react

Basic Setup

typescript
import { ReactFlow, Node, Edge, Background, Controls, MiniMap } from '@xyflow/react';import '@xyflow/react/dist/style.css';
const initialNodes: Node[] = [  {    id: '1',    type: 'input',    data: { label: 'Input Node' },    position: { x: 250, y: 5 },  },  {    id: '2',    data: { label: 'Default Node' },    position: { x: 100, y: 100 },  },  {    id: '3',    type: 'output',    data: { label: 'Output Node' },    position: { x: 400, y: 100 },  },];
const initialEdges: Edge[] = [  { id: 'e1-2', source: '1', target: '2', animated: true },  { id: 'e2-3', source: '2', target: '3' },];
function Flow() {  return (    <div style={{ width: '100vw', height: '100vh' }}>      <ReactFlow nodes={initialNodes} edges={initialEdges}>        <Background />        <Controls />        <MiniMap />      </ReactFlow>    </div>  );}
export default Flow;

Core Concepts

Nodes

Nodes are the building blocks of the graph. Each node has:

  • id: Unique identifier
  • type: Node type (built-in or custom)
  • position: { x, y } coordinates
  • data: Custom data object
typescript
import { Node } from '@xyflow/react';
const node: Node = {  id: 'node-1',  type: 'default',  position: { x: 100, y: 100 },  data: { label: 'Node Label' },  style: { background: '#D6D5E6' },  className: 'custom-node',};

Built-in node types:

  • default: Standard node
  • input: No target handles
  • output: No source handles
  • group: Container for other nodes

Edges

Edges connect nodes. Each edge requires:

  • id: Unique identifier
  • source: Source node ID
  • target: Target node ID
typescript
import { Edge } from '@xyflow/react';
const edge: Edge = {  id: 'e1-2',  source: '1',  target: '2',  type: 'smoothstep',  animated: true,  label: 'Edge Label',  style: { stroke: '#fff', strokeWidth: 2 },};

Built-in edge types:

  • default: Bezier curve
  • straight: Straight line
  • step: Orthogonal with sharp corners
  • smoothstep: Orthogonal with rounded corners

Handles

Handles are connection points on nodes. Use Position enum for placement:

typescript
import { Handle, Position } from '@xyflow/react';
<Handle type="target" position={Position.Top} /><Handle type="source" position={Position.Bottom} />

Available positions: Position.Top, Position.Right, Position.Bottom, Position.Left

State Management

Controlled Flow

Use state hooks for full control:

typescript
import { useNodesState, useEdgesState, addEdge, OnConnect } from '@xyflow/react';import { useCallback } from 'react';
function ControlledFlow() {  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const onConnect: OnConnect = useCallback(    (connection) => setEdges((eds) => addEdge(connection, eds)),    [setEdges]  );
  return (    <ReactFlow      nodes={nodes}      edges={edges}      onNodesChange={onNodesChange}      onEdgesChange={onEdgesChange}      onConnect={onConnect}    />  );}

useReactFlow Hook

Access the React Flow instance for programmatic control:

typescript
import { useReactFlow } from '@xyflow/react';
function FlowControls() {  const {    getNodes,    getEdges,    setNodes,    setEdges,    addNodes,    addEdges,    deleteElements,    fitView,    zoomIn,    zoomOut,    getNode,    getEdge,    updateNode,    updateEdge,  } = useReactFlow();
  return (    <button onClick={() => fitView()}>Fit View</button>  );}

Custom Nodes

Define custom nodes using NodeProps<T> with typed data:

typescript
import { NodeProps, Node, Handle, Position } from '@xyflow/react';
export type CustomNode = Node<{ label: string; status: 'active' | 'inactive' }, 'custom'>;
function CustomNodeComponent({ data, selected }: NodeProps<CustomNode>) {  return (    <div className={`px-4 py-2 ${selected ? 'ring-2' : ''}`}>      <Handle type="target" position={Position.Top} />      <div className="font-bold">{data.label}</div>      <Handle type="source" position={Position.Bottom} />    </div>  );}

Register with nodeTypes:

typescript
const nodeTypes: NodeTypes = { custom: CustomNodeComponent };<ReactFlow nodeTypes={nodeTypes} />

Key Patterns

  • Multiple Handles: Use id prop and style for positioning
  • Dynamic Handles: Call useUpdateNodeInternals([nodeId]) after adding/removing handles
  • Interactive Elements: Add className="nodrag" to prevent dragging on inputs/buttons

See Custom Nodes Reference for detailed patterns including styling, aviation map pins, and dynamic handles.

Custom Edges

Define custom edges using EdgeProps<T> and path utilities:

typescript
import { BaseEdge, EdgeProps, getBezierPath } from '@xyflow/react';
export type CustomEdge = Edge<{ status: 'normal' | 'error' }, 'custom'>;
function CustomEdgeComponent(props: EdgeProps<CustomEdge>) {  const [edgePath] = getBezierPath(props);
  return (    <BaseEdge      id={props.id}      path={edgePath}      style={{ stroke: props.data?.status === 'error' ? '#ef4444' : '#64748b' }}    />  );}

Path Utilities

  • getBezierPath() - Smooth curves
  • getStraightPath() - Straight lines
  • getSmoothStepPath() - Orthogonal with rounded corners
  • getSmoothStepPath({ borderRadius: 0 }) - Orthogonal with sharp corners (step edge)

All return [path, labelX, labelY, offsetX, offsetY].

Interactive Labels

Use EdgeLabelRenderer for HTML-based labels with pointer events:

typescript
import { EdgeLabelRenderer, BaseEdge, getBezierPath } from '@xyflow/react';
function ButtonEdge(props: EdgeProps) {  const [edgePath, labelX, labelY] = getBezierPath(props);  return (    <>      <BaseEdge id={props.id} path={edgePath} />      <EdgeLabelRenderer>        <div          style={{            position: 'absolute',            transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,            pointerEvents: 'all',          }}          className="nodrag nopan"        >          <button onClick={() => console.log('Delete')}>×</button>        </div>      </EdgeLabelRenderer>    </>  );}

See Custom Edges Reference for animated edges, time labels, and SVG text patterns.

Viewport Control

Use useReactFlow() hook for programmatic viewport control:

typescript
import { useReactFlow } from '@xyflow/react';
function ViewportControls() {  const { fitView, zoomIn, zoomOut, setCenter, screenToFlowPosition } = useReactFlow();
  // Fit all nodes in view  const handleFitView = () => fitView({ padding: 0.2, duration: 400 });
  // Zoom controls  const handleZoomIn = () => zoomIn({ duration: 300 });  const handleZoomOut = () => zoomOut({ duration: 300 });
  // Center on specific coordinates  const handleCenter = () => setCenter(250, 250, { zoom: 1.5, duration: 500 });
  // Convert screen coordinates to flow coordinates  const addNodeAtClick = (event: React.MouseEvent) => {    const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });    // Use position to add node  };
  return null;}

See Viewport Reference for save/restore state, controlled viewport, and coordinate transformations.

Events

React Flow provides comprehensive event handling:

Node Events

typescript
import { NodeMouseHandler, OnNodeDrag } from '@xyflow/react';
const onNodeClick: NodeMouseHandler = (event, node) => {  console.log('Node clicked:', node.id);};
const onNodeDrag: OnNodeDrag = (event, node, nodes) => {  console.log('Dragging:', node.id);};
<ReactFlow  onNodeClick={onNodeClick}  onNodeDrag={onNodeDrag}  onNodeDragStop={onNodeClick}/>

Edge and Connection Events

typescript
import { EdgeMouseHandler, OnConnect } from '@xyflow/react';
const onEdgeClick: EdgeMouseHandler = (event, edge) => console.log('Edge:', edge.id);const onConnect: OnConnect = (connection) => console.log('Connected:', connection);
<ReactFlow onEdgeClick={onEdgeClick} onConnect={onConnect} />

Selection and Viewport Events

typescript
import { useOnSelectionChange, useOnViewportChange } from '@xyflow/react';
useOnSelectionChange({  onChange: ({ nodes, edges }) => console.log('Selected:', nodes.length, edges.length),});
useOnViewportChange({  onChange: (viewport) => console.log('Viewport:', viewport.zoom),});

See Events Reference for complete event catalog including validation, deletion, and error handling.

Common Patterns

Preventing Drag/Pan

typescript
<input className="nodrag" /><button className="nodrag nopan">Click me</button>

Connection Validation

typescript
const isValidConnection = (connection: Connection) => {  return connection.source !== connection.target; // Prevent self-connections};
<ReactFlow isValidConnection={isValidConnection} />

Adding Nodes on Click

typescript
const { screenToFlowPosition, setNodes } = useReactFlow();
const onPaneClick = (event: React.MouseEvent) => {  const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });  setNodes(nodes => [...nodes, { id: `node-${Date.now()}`, position, data: { label: 'New' } }]);};

Updating Node Data

typescript
const { updateNodeData } = useReactFlow();updateNodeData('node-1', { label: 'Updated' });updateNodeData('node-1', (node) => ({ ...node.data, count: node.data.count + 1 }));

Provider Pattern

Wrap the app with ReactFlowProvider when using useReactFlow() outside the flow:

typescript
import { ReactFlow, ReactFlowProvider, useReactFlow } from '@xyflow/react';
function Controls() {  const { fitView } = useReactFlow(); // Must be inside provider  return <button onClick={() => fitView()}>Fit View</button>;}
function App() {  return (    <ReactFlowProvider>      <Controls />      <ReactFlow nodes={nodes} edges={edges} />    </ReactFlowProvider>  );}

Implementation gates

Use these sequenced checks before treating an integration as done (they target common footguns, not style preferences).

  1. CSS in the bundle — Ensure import '@xyflow/react/dist/style.css' runs in the app (entry or layout). Pass: nodes and edges have expected default styling; handles are visible and interactable.
  2. Stable nodeTypes / edgeTypes — Do not pass a fresh object literal every render; define maps outside the component or memoize with useMemo and correct deps. Pass: no remount flicker or “maximum update depth” / runaway updates when only selection or viewport changes.
  3. Provider boundary — Components that call useReactFlow() must be descendants of ReactFlowProvider, and the flow must actually mount. Pass: no missing-context error at runtime; programmatic APIs (fitView, etc.) work where expected.

Reference Files

For detailed implementation patterns, see:

  • Custom Nodes - NodeProps typing, Handle component, dynamic handles, styling patterns
  • Custom Edges - EdgeProps typing, path utilities, EdgeLabelRenderer, animated edges
  • Viewport - useReactFlow methods, fitView options, coordinate conversion
  • Events - Node/edge/connection events, selection handling, viewport changes

Source and attribution

Source:existential-birds/beagleinplugins/beagle-react/skills/react-flowat commitd1a7489

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal