--- name: react-flow description: 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. --- # 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 (
); } 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'; ``` 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 ( ); } ``` ### 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 ( ); } ``` ## Custom Nodes Define custom nodes using `NodeProps` 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) { return (
{data.label}
); } ``` Register with `nodeTypes`: ```typescript const nodeTypes: NodeTypes = { custom: CustomNodeComponent }; ``` ### 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](./references/custom-nodes.md) for detailed patterns including styling, aviation map pins, and dynamic handles. ## Custom Edges Define custom edges using `EdgeProps` and path utilities: ```typescript import { BaseEdge, EdgeProps, getBezierPath } from '@xyflow/react'; export type CustomEdge = Edge<{ status: 'normal' | 'error' }, 'custom'>; function CustomEdgeComponent(props: EdgeProps) { const [edgePath] = getBezierPath(props); return ( ); } ``` ### 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 ( <>
); } ``` See [Custom Edges Reference](./references/custom-edges.md) 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](./references/viewport.md) 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); }; ``` ### 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); ``` ### 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](./references/events.md) for complete event catalog including validation, deletion, and error handling. ## Common Patterns ### Preventing Drag/Pan ```typescript ``` ### Connection Validation ```typescript const isValidConnection = (connection: Connection) => { return connection.source !== connection.target; // Prevent self-connections }; ``` ### 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 ; } function App() { return ( ); } ``` ## Reference Files For detailed implementation patterns, see: - [Custom Nodes](./references/custom-nodes.md) - NodeProps typing, Handle component, dynamic handles, styling patterns - [Custom Edges](./references/custom-edges.md) - EdgeProps typing, path utilities, EdgeLabelRenderer, animated edges - [Viewport](./references/viewport.md) - useReactFlow methods, fitView options, coordinate conversion - [Events](./references/events.md) - Node/edge/connection events, selection handling, viewport changes