Edge Intersection
This example shows how you can detect an intersection between a dragged node and an edge.
The default
interactionWidth
of the edges is set to 75 via the
defaultEdgeOptions. We set
a wider interaction area so that we can easily find the edge and node
intersections.
import React, { useCallback, useRef } from 'react';
import {
  Background,
  Controls,
  ReactFlow,
  addEdge,
  useNodesState,
  useEdgesState,
  type OnConnect,
  type OnNodeDrag,
  useReactFlow,
  type Node,
  type Edge,
  type DefaultEdgeOptions,
} from '@xyflow/react';
 
import '@xyflow/react/dist/style.css';
 
const defaultEdgeOptions: DefaultEdgeOptions = {
  interactionWidth: 75,
};
 
const initialNodes: Node[] = [
  {
    id: 'a',
    type: 'input',
    position: { x: 200, y: 0 },
    data: { label: 'Node A' },
  },
  {
    id: 'b',
    position: { x: 0, y: 200 },
    data: { label: 'Node B' },
  },
  { id: 'c', position: { x: 0, y: 0 }, data: { label: 'Node C' } },
];
const initialEdges: Edge[] = [{ id: 'a->b', source: 'a', target: 'b' }];
 
export default function App() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const onConnect: OnConnect = useCallback(
    (connection) => setEdges((edges) => addEdge(connection, edges)),
    [setEdges],
  );
 
  const { updateEdge, getEdge, addEdges } = useReactFlow();
 
  const overlappedEdgeRef = useRef<string | null>(null);
 
  const onNodeDragStop: OnNodeDrag = useCallback(
    (event, node) => {
      const edgeId = overlappedEdgeRef.current;
      if (!edgeId) return;
      const edge = getEdge(edgeId);
      if (!edge) return;
 
      updateEdge(edgeId, { source: edge.source, target: node.id, style: {} });
 
      addEdges({
        id: `${node.id}->${edge.target}`,
        source: node.id,
        target: edge.target,
      });
 
      overlappedEdgeRef.current = null;
    },
    [getEdge, addEdges, updateEdge],
  );
 
  const onNodeDrag: OnNodeDrag = useCallback(
    (e, node) => {
      const nodeDiv = document.querySelector(
        `.react-flow__node[data-id=${node.id}]`,
      );
 
      if (!nodeDiv) return;
 
      const rect = nodeDiv.getBoundingClientRect();
      const centerX = rect.left + rect.width / 2;
      const centerY = rect.top + rect.height / 2;
 
      const edgeFound = document
        .elementsFromPoint(centerX, centerY)
        .find((el) =>
          el.classList.contains('react-flow__edge-interaction'),
        )?.parentElement;
 
      const edgeId = edgeFound?.dataset.id;
 
      if (edgeId) updateEdge(edgeId, { style: { stroke: 'black' } });
      else if (overlappedEdgeRef.current)
        updateEdge(overlappedEdgeRef.current, { style: {} });
 
      overlappedEdgeRef.current = edgeId || null;
    },
    [updateEdge],
  );
 
  return (
    <ReactFlow
      nodes={nodes}
      onNodesChange={onNodesChange}
      edges={edges}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
      onNodeDragStop={onNodeDragStop}
      onNodeDrag={onNodeDrag}
      defaultEdgeOptions={defaultEdgeOptions}
      fitView
    >
      <Background />
      <Controls />
    </ReactFlow>
  );
}Last updated on