Skip to Content
ExamplesInteraction

Drag and Drop

In these examples, we use a custom Sidebar component to create new nodes when dragging from the sidebar into the React Flow pane.

A drag and drop user interface is very common for node-based workflow editors. The drag and drop behavior outside of the React Flow pane is not built in but can be implemented with the native HTML Drag and Drop API , Pointer Events , or a third party library like react-draggable .

Drag and Drop with HTML Drag and Drop API

The HTML Drag and Drop API is a native API that is supported by all major browsers. It is a simple API that allows you to drag and drop elements between different elements on the page.

Note: HTML Drag and Drop API is not properly supported on touch devices. See the example below for an alternative approach using pointer events.

import React, { useRef, useCallback } from 'react'; import { ReactFlow, ReactFlowProvider, addEdge, useNodesState, useEdgesState, Controls, useReactFlow, Background, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import Sidebar from './Sidebar'; import { DnDProvider, useDnD } from './DnDContext'; const initialNodes = [ { id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 }, }, ]; let id = 0; const getId = () => `dndnode_${id++}`; const DnDFlow = () => { const reactFlowWrapper = useRef(null); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { screenToFlowPosition } = useReactFlow(); const [type] = useDnD(); const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []); const onDragOver = useCallback((event) => { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; }, []); const onDrop = useCallback( (event) => { event.preventDefault(); // check if the dropped element is valid if (!type) { return; } // project was renamed to screenToFlowPosition // and you don't need to subtract the reactFlowBounds.left/top anymore // details: https://reactflow.dev/whats-new/2023-11-10 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, type], ); const onDragStart = (event, nodeType) => { setType(nodeType); event.dataTransfer.setData('text/plain', nodeType); event.dataTransfer.effectAllowed = 'move'; }; return ( <div className="dndflow"> <div className="reactflow-wrapper" ref={reactFlowWrapper}> <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDrop={onDrop} onDragStart={onDragStart} onDragOver={onDragOver} fitView > <Controls /> <Background /> </ReactFlow> </div> <Sidebar /> </div> ); }; export default () => ( <ReactFlowProvider> <DnDProvider> <DnDFlow /> </DnDProvider> </ReactFlowProvider> );

Drag and Drop with Pointer Events

Another way to implement drag and drop is to use pointer events.

The Pointer Events API is a modern API that is supported by all major browsers. It is slightly more complex to implement than the HTML Drag and Drop API, but by using pointer events we can make sure that the drag and drop behavior is consistent across all devices, both mouse and touch.

import { useCallback } from 'react'; import { Background, Connection, Controls, ReactFlow, ReactFlowProvider, addEdge, useEdgesState, useNodesState, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import { Sidebar } from './Sidebar'; import { DnDProvider } from './useDnD'; const initialNodes = [ { id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 }, }, ]; function DnDFlow() { const [nodes, _, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const onConnect = useCallback( (params: Connection) => setEdges((eds) => addEdge(params, eds)), [], ); return ( <div className="dndflow"> <div className="reactflow-wrapper"> <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} fitView > <Controls /> <Background /> </ReactFlow> </div> <Sidebar /> </div> ); } export default () => ( <ReactFlowProvider> <DnDProvider> <DnDFlow /> </DnDProvider> </ReactFlowProvider> );

Drag and Drop with Neodrag

Neodrag  is a library that provides a simple and easy to use drag and drop API. In a real-world application, you may want to use the Neodrag library to handle the drag and drop behavior in a cross-platform compatible way, compatible with both mouse and touch devices.

import { useCallback } from 'react'; import { Background, Connection, Controls, ReactFlow, ReactFlowProvider, addEdge, useEdgesState, useNodesState, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import { Sidebar } from './Sidebar'; const initialNodes = [ { id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 }, }, ]; function DnDFlow() { const [nodes, _, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const onConnect = useCallback( (params: Connection) => setEdges((eds) => addEdge(params, eds)), [], ); return ( <div className="dndflow"> <div className="reactflow-wrapper"> <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} fitView > <Controls /> <Background /> </ReactFlow> </div> <Sidebar /> </div> ); } export default () => ( <ReactFlowProvider> <DnDFlow /> </ReactFlowProvider> );
Last updated on