Adding Interactivity
Now that we’ve built our first flow, let’s add interactivity so you can select, drag, and remove nodes and edges.
In this Getting Started tutorial, we’ll use React Flow as a “controlled component” . This is typically the most flexible and reliable approach for integrating it into your applications. (If you prefer, you can also work with React Flow in an uncontrolled way.
Handling Change Events
Continuing from Building a Flow, let’s dive into some essentials. To manage changes, we’ll be using useState
with two helper functions from React Flow: applyEdgeChanges
and applyNodeChanges
. We can import these into our project like this:
import { useState, useCallback } from 'react';
import { ReactFlow, applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
Next, we’re going to set up states for both the nodes and edges:
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
Directly beneath that, we’ll add these two functions:
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[],
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[],
);
When you drag or select a node, the onNodesChange
handler is triggered. The applyNodeChanges
function then uses these change events to update the current state of your nodes. Here’s how it all comes together. Try clicking and dragging a node to move it around and watch the UI update in real time.
import { useState, useCallback } from 'react';
import {
ReactFlow,
Controls,
Background,
applyNodeChanges,
applyEdgeChanges,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{
id: '1',
data: { label: 'Hello' },
position: { x: 0, y: 0 },
type: 'input',
},
{
id: '2',
data: { label: 'World' },
position: { x: 100, y: 100 },
},
];
const initialEdges = [
{ id: '1-2', source: '1', target: '2', label: 'to the', type: 'step' },
];
function Flow() {
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)),
[],
);
return (
<div style={{ height: '100%' }}>
<ReactFlow
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
onEdgesChange={onEdgesChange}
fitView
>
<Background />
<Controls />
</ReactFlow>
</div>
);
}
export default Flow;
Handle Connections
One last piece is missing: connecting nodes manually. For this, we need to implement an onConnect
handler and pass it to the <ReactFlow />
component:
import { useState, useCallback } from 'react';
import {
ReactFlow,
Controls,
Background,
applyNodeChanges,
applyEdgeChanges,
addEdge,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{
id: '1',
data: { label: 'Hello' },
position: { x: 0, y: 0 },
type: 'input',
},
{
id: '2',
data: { label: 'World' },
position: { x: 100, y: 100 },
},
];
const initialEdges = [];
function Flow() {
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)),
[],
);
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[],
);
return (
<div style={{ height: '100%' }}>
<ReactFlow
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
>
<Background />
<Controls />
</ReactFlow>
</div>
);
}
export default Flow;
Try to connect the two nodes by dragging from on handle to another one. That’s it. You’ve built a fully interactive flow.
That’s it for now :) You made it! If you want to move on, we recommend to check out the “Custom Nodes” guide.