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
Implement drag-and-drop to add nodes to the canvas by transforming screen coordinates to flow coordinates using screenToFlowPosition().
Incorrect (wrong coordinates - doesn't account for zoom/pan):
const onDrop = (event: React.DragEvent) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
// ❌ Uses screen coordinates directly - wrong when zoomed or panned
const newNode = {
id: getId(),
type,
position: { x: event.clientX, y: event.clientY },
data: { label: `${type} node` },
};
setNodes((nds) => nds.concat(newNode));
};
// Node appears in wrong location when canvas is zoomed or panned!Correct (transforms screen to flow coordinates):
import { useCallback } from 'react';
import {
useReactFlow,
ReactFlow,
useNodesState,
useEdgesState
} from '@xyflow/react';
let id = 0;
const getId = () => `node_${id++}`;
function DragDropFlow() {
const { screenToFlowPosition } = useReactFlow();
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onDragOver = useCallback((event: React.DragEvent) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = useCallback(
(event: React.DragEvent) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
if (!type) return;
// ✅ Transform screen coordinates to flow coordinates
const position = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const newNode = {
id: getId(),
type,
position,
data: { label: `${type} node` },
};
setNodes((nds) => nds.concat(newNode));
},
[screenToFlowPosition, setNodes]
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onDrop={onDrop}
onDragOver={onDragOver}
fitView
/>
);
}Draggable Sidebar Component:
function Sidebar() {
const onDragStart = (event: React.DragEvent, nodeType: string) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};
return (
<aside className="sidebar">
<div className="description">Drag nodes to the canvas</div>
<div
className="node-type"
onDragStart={(e) => onDragStart(e, 'input')}
draggable
>
Input Node
</div>
<div
className="node-type"
onDragStart={(e) => onDragStart(e, 'default')}
draggable
>
Default Node
</div>
<div
className="node-type"
onDragStart={(e) => onDragStart(e, 'output')}
draggable
>
Output Node
</div>
</aside>
);
}With Custom Data:
const onDragStart = (event: React.DragEvent, nodeData: any) => {
event.dataTransfer.setData(
'application/reactflow',
JSON.stringify(nodeData)
);
event.dataTransfer.effectAllowed = 'move';
};
const onDrop = useCallback(
(event: React.DragEvent) => {
event.preventDefault();
const dataStr = event.dataTransfer.getData('application/reactflow');
if (!dataStr) return;
const nodeData = JSON.parse(dataStr);
const position = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const newNode = {
id: getId(),
...nodeData,
position,
};
setNodes((nds) => nds.concat(newNode));
},
[screenToFlowPosition, setNodes]
);Complete Example with Provider:
import { ReactFlowProvider } from '@xyflow/react';
export default function App() {
return (
<ReactFlowProvider>
<div className="app">
<Sidebar />
<DragDropFlow />
</div>
</ReactFlowProvider>
);
}
// ReactFlowProvider needed for screenToFlowPosition to workAdditional Context:
screenToFlowPosition() accounts for zoom level and pan positiononDragOver to allow dropdraggable attribute on source elements'application/reactflow' as data type conventionReactFlowProvider if using hooks outside <ReactFlow>Reference: Drag and Drop Example