Core Concepts
In the following part we will introduce you to the core concepts of React Flow and explain how to create an interactive flow. A flow consists of nodes and edges (or just nodes). You can pass arrays of nodes
and edges
as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label (this could be different if you are using custom nodes) and an edge needs a source (node id) and a target (node id). You can read more about the options in the Node options and Edge options sections.
Controlled or Uncontrolled
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow. In the following part we will setup a controlled flow. Let’s start by adding some nodes and edges to the ReactFlow component:
The dimensions of your React Flow component depend on the parent dimensions. That means that the parent needs a width and height to render React Flow properly.
import { useState } from 'react';
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'Input Node' },
position: { x: 250, y: 25 },
},
{
id: '2',
// you can also pass a React component as a label
data: { label: <div>Default Node</div> },
position: { x: 100, y: 125 },
},
{
id: '3',
type: 'output',
data: { label: 'Output Node' },
position: { x: 250, y: 250 },
},
];
const initialEdges = [
{ id: 'e1-2', source: '1', target: '2' },
{ id: 'e2-3', source: '2', target: '3', animated: true },
];
function Flow() {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
return <ReactFlow nodes={nodes} edges={edges} fitView />;
}
export default Flow;
Basic Functionality
By default React Flow doesn’t do any internal state updates besides handling the viewport when you setup a controlled flow. As with an <input />
component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange
and an onEdgesChange
handler:
import { useCallback, useState } from 'react';
import { ReactFlow, applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { initialNodes } from './nodes';
import { initialEdges } from './edges';
function Flow() {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes],
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges],
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
fitView
/>
);
}
export default Flow;
What is happening here? Whenever React Flow triggers a change (node init, node drag, edge select, etc.), the onNodesChange
handler gets called. We export an applyNodeChanges
handler so that you don’t need to handle the changes by yourself. The applyNodeChanges
handler returns an updated array of nodes that is your new nodes array. You now have an interactive flow with the following kinds of interactions:
- selectable nodes and edges
- draggable nodes
- removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the
deleteKeyCode
prop) - multi-selection area by pressing Shift (that’s the default
selectionKeyCode
) - multi-selection by pressing command (that’s the default
multiSelectionKeyCode
)
For convenience we export the helper hooks useNodesState
and useEdgesState
that you can use to create the nodes and edges state:
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
Connecting Nodes
The last piece that is missing to get the full interactivity is the onConnect
handler. You need to implement it, in order to handle new connections.
import { useCallback, useState } from 'react';
import {
ReactFlow,
addEdge,
applyEdgeChanges,
applyNodeChanges,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { initialNodes } from './nodes';
import { initialEdges } from './edges';
function Flow() {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes],
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges],
);
const onConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges],
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
/>
);
}
export default Flow;
In this example we are using the addEdge
handler that returns an array of edges with the newly created one. If you want to set a certain edge option whenever an edge gets created you pass your options like this:
const onConnect = useCallback(
(connection) =>
setEdges((eds) => addEdge({ ...connection, animated: true }, eds)),
[setEdges],
);
or use the defaultEdgeOptions
prop:
const defaultEdgeOptions = { animated: true };
...
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
defaultEdgeOptions={defaultEdgeOptions}
/>;