--- name: react-flow-implementation description: Implements React Flow node-based UIs correctly using @xyflow/react. Use when building flow charts, diagrams, visual editors, or node-based applications with React. Covers nodes, edges, handles, custom components, state management, and viewport control. --- # React Flow Implementation ## Quick Start ```tsx import { ReactFlow, useNodesState, useEdgesState, addEdge } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; const initialNodes = [ { id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } }, { id: '2', position: { x: 200, y: 100 }, data: { label: 'Node 2' } }, ]; const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }]; export default function Flow() { const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const onConnect = useCallback( (connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges] ); return (
); } ``` ## Core Patterns ### TypeScript Types ```typescript import type { Node, Edge, NodeProps, BuiltInNode } from '@xyflow/react'; // Define custom node type with data shape type CustomNode = Node<{ value: number; label: string }, 'custom'>; // Combine with built-in nodes type MyNode = CustomNode | BuiltInNode; type MyEdge = Edge<{ weight?: number }>; // Use throughout app const [nodes, setNodes] = useNodesState(initialNodes); ``` ### Custom Nodes ```tsx import { memo } from 'react'; import { Handle, Position, type NodeProps } from '@xyflow/react'; // Define node type type CounterNode = Node<{ count: number }, 'counter'>; // Always wrap in memo for performance const CounterNode = memo(function CounterNode({ data, isConnectable }: NodeProps) { return ( <>
Count: {data.count} {/* nodrag prevents dragging when interacting with button */}
); }); // Register in nodeTypes (define OUTSIDE component to avoid re-renders) const nodeTypes = { counter: CounterNode }; // Use in ReactFlow ``` ### Multiple Handles ```tsx // Use handle IDs when a node has multiple handles of same type // Connect with specific handles const edge = { id: 'e1-2', source: '1', sourceHandle: 'a', target: '2', targetHandle: null }; ``` ### Custom Edges ```tsx import { BaseEdge, EdgeProps, getSmoothStepPath } from '@xyflow/react'; function CustomEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data }: EdgeProps) { const [edgePath, labelX, labelY] = getSmoothStepPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition, }); return ( <> {data?.label} ); } const edgeTypes = { custom: CustomEdge }; ``` ## State Management ### Controlled (Recommended for Production) ```tsx // External state with change handlers const [nodes, setNodes] = useState(initialNodes); const [edges, setEdges] = useState(initialEdges); const onNodesChange = useCallback( (changes) => setNodes((nds) => applyNodeChanges(changes, nds)), [] ); const onEdgesChange = useCallback( (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), [] ); ``` ### Using useReactFlow ```tsx import { useReactFlow, ReactFlowProvider } from '@xyflow/react'; function FlowControls() { const { getNodes, setNodes, addNodes, updateNodeData, getEdges, setEdges, addEdges, fitView, zoomIn, zoomOut, setViewport, deleteElements, toObject, } = useReactFlow(); const addNode = () => { addNodes({ id: `${Date.now()}`, position: { x: 100, y: 100 }, data: { label: 'New' } }); }; return ; } // Must wrap in provider when using useReactFlow function App() { return ( ); } ``` ### Updating Node Data ```tsx const { updateNodeData } = useReactFlow(); // Merge with existing data updateNodeData(nodeId, { label: 'Updated' }); // Replace data entirely updateNodeData(nodeId, { newField: 'value' }, { replace: true }); ``` ## Viewport & Fit View ```tsx // Fit on initial render // Programmatic control const { fitView, setViewport, getViewport, zoomTo } = useReactFlow(); // Fit to specific nodes fitView({ nodes: [{ id: '1' }, { id: '2' }], duration: 500 }); // Set exact viewport setViewport({ x: 100, y: 100, zoom: 1.5 }, { duration: 300 }); ``` ## Connection Validation ```tsx const isValidConnection = useCallback((connection: Connection) => { // Prevent self-connections if (connection.source === connection.target) return false; // Custom validation logic const sourceNode = getNode(connection.source); const targetNode = getNode(connection.target); return sourceNode?.type !== targetNode?.type; }, []); ``` ## Common Props Reference ```tsx ``` ## CSS Classes for Interaction | Class | Effect | |-------|--------| | `nodrag` | Prevent dragging when clicking element | | `nowheel` | Prevent zoom on wheel events | | `nopan` | Prevent panning from element | | `nokey` | Prevent keyboard events (use on inputs) | ## Additional Components See [ADDITIONAL_COMPONENTS.md](ADDITIONAL_COMPONENTS.md) for MiniMap, Controls, Background, NodeToolbar, NodeResizer.