Comprehensive React Flow (@xyflow/react) patterns and best practices for building node-based UIs, workflow editors, and interactive diagrams. Use when working with React Flow for (1) building flow editors or node-based interfaces, (2) creating custom nodes and edges, (3) implementing drag-and-drop workflows, (4) optimizing performance for large graphs, (5) managing flow state and interactions, (6) implementing auto-layout or positioning, or (7) TypeScript integration with React Flow.
95
95%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Comprehensive patterns and best practices for building production-ready node-based UIs with React Flow (@xyflow/react v12+).
import { useCallback } from 'react';
import {
ReactFlow,
Background,
Controls,
MiniMap,
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' } },
];
const initialEdges = [
{ id: 'e1-2', source: '1', target: '2' },
];
// Define outside component or use useMemo
const nodeTypes = { custom: CustomNode };
const edgeTypes = { custom: CustomEdge };
function Flow() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
return (
<div style={{ width: '100%', height: '100vh' }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
fitView
>
<Background />
<Controls />
<MiniMap />
</ReactFlow>
</div>
);
}import { memo } from 'react';
import { Handle, Position, NodeProps } from '@xyflow/react';
// Node props: id, position: { x, y }, data, type, parentId, extent, draggable/selectable/connectable
const CustomNode = memo(({ data, selected }: NodeProps) => {
return (
<div className={`custom-node ${selected ? 'selected' : ''}`}>
<Handle type="target" position={Position.Top} />
<div>{data.label}</div>
<Handle type="source" position={Position.Bottom} />
</div>
);
});
// IMPORTANT: Define outside component
const nodeTypes = { custom: CustomNode };import { BaseEdge, EdgeLabelRenderer, getBezierPath, EdgeProps } from '@xyflow/react';
// Edge props: id, source, target, sourceHandle/targetHandle, type ('default' | 'straight' | 'step' | 'smoothstep'), animated, label, markerStart/markerEnd
function CustomEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }: EdgeProps) {
const [edgePath, labelX, labelY] = getBezierPath({
sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition,
});
return (
<>
<BaseEdge path={edgePath} />
<EdgeLabelRenderer>
<div style={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}>
Custom Label
</div>
</EdgeLabelRenderer>
</>
);
}// 1. Memoize node/edge types (define outside component)
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
// 2. Memoize callbacks
const onConnect = useCallback((params) =>
setEdges((eds) => addEdge(params, eds)), [setEdges]
);
// 3. Use simple edge types for large graphs
const edgeType = nodes.length > 100 ? 'straight' : 'smoothstep';
// 4. Avoid unnecessary re-renders in custom components
const CustomNode = memo(({ data }) => <div>{data.label}</div>);useReactFlow() - Access flow instance methods (getNodes, setNodes, fitView, etc.)useNodesState() / useEdgesState() - Managed state with change handlersuseNodes() / useEdges() - Reactive access to current nodes/edgesuseNodesData(id) - Get specific node data (more performant than useNodes)useHandleConnections() - Get connections for a handleuseConnection() - Track connection in progressuseStore() - Direct store access (use sparingly)const onDrop = useCallback((event) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
// Validate that a recognized type was dragged
if (!type) return;
const position = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const newNode = {
id: getId(),
type,
position,
data: { label: `${type} node` },
};
setNodes((nds) => {
const updated = nds.concat(newNode);
// Verify node was added; log a warning if the id already existed
if (updated.length === nds.length) {
console.warn(`Node with id ${newNode.id} already exists — skipping.`);
}
return updated;
});
}, [screenToFlowPosition]);
// Also wire up onDragOver to enable drop
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);const { toObject } = useReactFlow();
// Save
const saveFlow = () => {
try {
const flow = toObject();
localStorage.setItem('flow', JSON.stringify(flow));
} catch (err) {
console.error('Failed to save flow:', err);
}
};
// Restore
const restoreFlow = () => {
try {
const raw = localStorage.getItem('flow');
if (!raw) return;
const flow = JSON.parse(raw);
// Validate expected structure before applying
if (!Array.isArray(flow.nodes) || !Array.isArray(flow.edges)) return;
setNodes(flow.nodes);
setEdges(flow.edges);
if (flow.viewport) setViewport(flow.viewport);
// Verify restored flow renders correctly: call fitView after state updates settle
setTimeout(() => fitView(), 0);
} catch (err) {
console.error('Failed to restore flow:', err);
// On error, reset to a known-good empty state
setNodes([]);
setEdges([]);
}
};const isValidConnection = useCallback((connection) => {
// Prevent self-connections
if (connection.source === connection.target) return false;
// Custom validation logic
return true;
}, []);For comprehensive patterns and best practices, see individual rule files in the rules/ directory organized by category:
rules/setup-*.md - Critical setup patterns
rules/perf-*.md - Performance optimization
rules/node-*.md - Node customization patterns
rules/edge-*.md - Edge handling patterns
rules/state-*.md - State management
rules/hook-*.md - Hooks usage
rules/layout-*.md - Layout and positioning
rules/interaction-*.md - User interactions
rules/typescript-*.md - TypeScript integrationFor the complete guide with all rules and examples expanded: see AGENTS.md
Comprehensive scraped documentation from reactflow.dev is available in scraped/:
scraped/learn-concepts/, scraped/learn-customization/, scraped/learn-advanced/scraped/api-hooks/, scraped/api-types/, scraped/api-utils/, scraped/api-components/scraped/examples-nodes/, scraped/examples-edges/, scraped/examples-interaction/, scraped/examples-layout/scraped/ui-components/scraped/learn-tutorials/scraped/learn-troubleshooting/onConnect handlernodesDraggable prop@xyflow/react/dist/style.css<ReactFlowProvider>useReactFlow<NodeType, EdgeType>()@xyflow/react (npm)