Skills react-flow
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.
git clone https://github.com/openclaw/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/anderskev/react-flow" ~/.claude/skills/clawdbot-skills-react-flow && rm -rf "$T"
skills/anderskev/react-flow/SKILL.mdReact 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
pnpm add @xyflow/react
Basic Setup
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:
: Unique identifierid
: Node type (built-in or custom)type
: { x, y } coordinatesposition
: Custom data objectdata
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:
: Standard nodedefault
: No target handlesinput
: No source handlesoutput
: Container for other nodesgroup
Edges
Edges connect nodes. Each edge requires:
: Unique identifierid
: Source node IDsource
: Target node IDtarget
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:
: Bezier curvedefault
: Straight linestraight
: Orthogonal with sharp cornersstep
: Orthogonal with rounded cornerssmoothstep
Handles
Handles are connection points on nodes. Use
Position enum for placement:
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:
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:
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:
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:
const nodeTypes: NodeTypes = { custom: CustomNodeComponent }; <ReactFlow nodeTypes={nodeTypes} />
Key Patterns
- Multiple Handles: Use
prop andid
for positioningstyle - Dynamic Handles: Call
after adding/removing handlesuseUpdateNodeInternals([nodeId]) - Interactive Elements: Add
to prevent dragging on inputs/buttonsclassName="nodrag"
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:
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
- Smooth curvesgetBezierPath()
- Straight linesgetStraightPath()
- Orthogonal with rounded cornersgetSmoothStepPath()
- Orthogonal with sharp corners (step edge)getSmoothStepPath({ borderRadius: 0 })
All return
[path, labelX, labelY, offsetX, offsetY].
Interactive Labels
Use
EdgeLabelRenderer for HTML-based labels with pointer events:
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:
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
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
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
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
<input className="nodrag" /> <button className="nodrag nopan">Click me</button>
Connection Validation
const isValidConnection = (connection: Connection) => { return connection.source !== connection.target; // Prevent self-connections }; <ReactFlow isValidConnection={isValidConnection} />
Adding Nodes on Click
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
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:
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> ); }
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