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
Use the isValidConnection prop to validate connections before they're created, preventing invalid graph structures like cycles, self-connections, or incompatible types.
Incorrect (no validation):
<ReactFlow
nodes={nodes}
edges={edges}
onConnect={onConnect}
// ❌ No validation - users can create any connection
/>
// Users can create cycles, self-loops, duplicate connectionsCorrect (prevent self-connections):
import { useCallback } from 'react';
import { ReactFlow, Connection } from '@xyflow/react';
function Flow() {
const isValidConnection = useCallback((connection: Connection) => {
// ✅ Prevent nodes from connecting to themselves
return connection.source !== connection.target;
}, []);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onConnect={onConnect}
isValidConnection={isValidConnection}
/>
);
}Prevent Cycles (Directed Acyclic Graph):
import { useCallback } from 'react';
import { ReactFlow, useReactFlow, getOutgoers } from '@xyflow/react';
function Flow() {
const { getNode, getNodes, getEdges } = useReactFlow();
const isValidConnection = useCallback(
(connection: Connection) => {
// Prevent self-connections
if (connection.source === connection.target) return false;
// Check for cycles
const hasCycle = (nodeId: string, visited = new Set<string>()): boolean => {
if (visited.has(nodeId)) return true;
visited.add(nodeId);
const node = getNode(nodeId);
if (!node) return false;
const outgoers = getOutgoers(node, getNodes(), getEdges());
return outgoers.some((outgoer) => hasCycle(outgoer.id, new Set(visited)));
};
return !hasCycle(connection.target);
},
[getNode, getNodes, getEdges]
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onConnect={onConnect}
isValidConnection={isValidConnection}
/>
);
}Validate Based on Handle Types:
const isValidConnection = useCallback(
(connection: Connection) => {
const sourceNode = getNode(connection.source);
const targetNode = getNode(connection.target);
if (!sourceNode || !targetNode) return false;
// Check handle compatibility
const sourceHandle = connection.sourceHandle;
const targetHandle = connection.targetHandle;
// Example: Only allow data outputs to connect to data inputs
if (sourceHandle?.startsWith('data-') && !targetHandle?.startsWith('data-')) {
return false;
}
return true;
},
[getNode]
);Prevent Duplicate Connections:
const isValidConnection = useCallback(
(connection: Connection) => {
const edges = getEdges();
// Check if connection already exists
const isDuplicate = edges.some(
(edge) =>
edge.source === connection.source &&
edge.target === connection.target &&
edge.sourceHandle === connection.sourceHandle &&
edge.targetHandle === connection.targetHandle
);
return !isDuplicate;
},
[getEdges]
);Additional Context:
isValidConnection is called before a connection is createdtrue to allow the connection, false to reject ituseCallback to avoid re-creating the function on every renderReference: Connection Validation