Create onConnect handler

The onConnect handler is called whenever a new connection is made between two nodes. We can use the addEdge utility function to create a new edge and update the edge Array.

const onConnect = useCallback(
  (params) => setEdges((edgesSnapshot) => addEdge(params, edgesSnapshot)),
  [],
);
Pass it to ReactFlow

Now we can pass the onConnect handler to the <ReactFlow /> component:

<ReactFlow
  nodes={nodes}
  edges={edges}
  onNodesChange={onNodesChange}
  onEdgesChange={onEdgesChange}
  onConnect={onConnect}
  fitView
>
  <Background />
  <Controls />
</ReactFlow>
Connectable flow

Try to connect the two nodes by dragging from on handle to another one. The onConnect handler will be triggered, and the new edge will be added to the flow. 🥳

Example: learn/make-it-interactive-2

App.jsx
import { useState, useCallback } from 'react';
import {
  ReactFlow,
  Controls,
  Background,
  applyNodeChanges,
  applyEdgeChanges,
  addEdge,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
const initialNodes = [
  {
    id: 'n1',
    data: { label: 'Node 1' },
    position: { x: 0, y: 0 },
    type: 'input',
  },
  {
    id: 'n2',
    data: { label: 'Node 2' },
    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}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        fitView
        colorMode="system"
      >
        <Background />
        <Controls />
      </ReactFlow>
    </div>
  );
}
 
export default Flow;
index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Flow Example</title>
    <style>
      html,
      body {
        margin: 0;
        font-family: sans-serif;
      }
 
      #app {
        width: 100vw;
        height: 100vh;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="./index.jsx"></script>
  </body>
</html>
index.jsx
import { createRoot } from 'react-dom/client';
import App from './App';
 
const container = document.querySelector('#app');
const root = createRoot(container);
 
root.render(<App />);

Full code example 🏁

Example: learn/make-it-interactive-2

App.jsx
import { useState, useCallback } from 'react';
import {
  ReactFlow,
  Controls,
  Background,
  applyNodeChanges,
  applyEdgeChanges,
  addEdge,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
const initialNodes = [
  {
    id: 'n1',
    data: { label: 'Node 1' },
    position: { x: 0, y: 0 },
    type: 'input',
  },
  {
    id: 'n2',
    data: { label: 'Node 2' },
    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}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        fitView
        colorMode="system"
      >
        <Background />
        <Controls />
      </ReactFlow>
    </div>
  );
}
 
export default Flow;
index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Flow Example</title>
    <style>
      html,
      body {
        margin: 0;
        font-family: sans-serif;
      }
 
      #app {
        width: 100vw;
        height: 100vh;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="./index.jsx"></script>
  </body>
</html>
index.jsx
import { createRoot } from 'react-dom/client';
import App from './App';
 
const container = document.querySelector('#app');
const root = createRoot(container);
 
root.render(<App />);

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 capabilities:

  • selectable nodes and edges
  • draggable nodes
  • connectable nodes by dragging from one node handle to another
  • multi-selection area by pressing shift — the default selectionKeyCode
  • multi-selection by pressing cmd — the default multiSelectionKeyCode
  • removing selected elements by pressing backspace — the default deleteKeyCode

If you want to jump straight into creating your own application, we recommend checking out the Customization section. Otherwise keep reading to learn more about React Flows capabilities.