ExamplesEdges

Animating Edges

React Flow provides a simple built-in animation for the default edge type, but it is possible to create more advanced animations by using custom edges. Below are a collection of examples showing different ways an edge path might be used in

Animating SVG elements

It is possible to animate an SVG element along a path using the <animateMotion /> element. This example creates a custom edge that animates a circle along the edge path.

import React from 'react';
import { BaseEdge, getSmoothStepPath, type EdgeProps } from '@xyflow/react';
 
export function AnimatedSVGEdge({
  id,
  sourceX,
  sourceY,
  targetX,
  targetY,
  sourcePosition,
  targetPosition,
}: EdgeProps) {
  const [edgePath] = getSmoothStepPath({
    sourceX,
    sourceY,
    sourcePosition,
    targetX,
    targetY,
    targetPosition,
  });
 
  return (
    <>
      <BaseEdge id={id} path={edgePath} />
      <circle r="10" fill="#ff0073">
        <animateMotion dur="2s" repeatCount="indefinite" path={edgePath} />
      </circle>
    </>
  );
}

Animating other nodes

If you want to animate more than SVG along an edge, you can do that with the Web Animations API. This example demonstrates how to animate another node in the flow along an edge path by using the offsetPath property and animating the offsetDistance property.

import React, { useEffect, useMemo } from 'react';
import {
  BaseEdge,
  getBezierPath,
  useReactFlow,
  type Edge,
  type EdgeProps,
} from '@xyflow/react';
 
export type AnimatedNodeEdge = Edge<{ node: string }, 'animatedNode'>;
 
export function AnimatedNodeEdge({
  id,
  data = { node: '' },
  sourceX,
  sourceY,
  targetX,
  targetY,
  sourcePosition,
  targetPosition,
}: EdgeProps<AnimatedNodeEdge>) {
  const { getNode, updateNode } = useReactFlow();
  const [edgePath] = getBezierPath({
    sourceX,
    sourceY,
    sourcePosition,
    targetX,
    targetY,
    targetPosition,
  });
  const selector = useMemo(
    () => `.react-flow__node[data-id="${data.node}"]`,
    [data.node],
  );
 
  useEffect(() => {
    const node = document.querySelector(selector) as HTMLElement;
 
    if (!node) return;
 
    node.style.offsetPath = `path('${edgePath}')`;
    node.style.offsetRotate = '0deg';
    // This property is fairly new and not all versions of TypeScript have it
    // in the lib.dom.d.ts file. If you get an error here, you can either
    // ignore it or add the property to the CSSStyleDeclaration interface
    // yourself.
    //
    // @ts-expect-error
    node.style.offsetAnchor = 'center';
 
    let wasDraggable = getNode(data.node).draggable;
 
    updateNode(data.node, { draggable: false });
 
    return () => {
      node.style.offsetPath = 'none';
      updateNode(data.node, { draggable: wasDraggable });
    };
  }, [selector, edgePath]);
 
  useEffect(() => {
    const node = document.querySelector(selector) as HTMLElement;
 
    if (!node) return;
 
    const keyframes = [{ offsetDistance: '0%' }, { offsetDistance: '100%' }];
    const animation = node.animate(keyframes, {
      duration: 2000,
      direction: 'alternate',
      iterations: Infinity,
    });
 
    return () => {
      animation.cancel();
    };
  }, [selector]);
 
  return <BaseEdge id={id} path={edgePath} />;
}

There are some important details in this example to take note of:

  • The animated node has its draggable property set to false while the animation is running. This prevents a user moving the node around and breaking the animation path.

  • The animation path and the animation itself are set up in separate useEffect hooks. This lets the animation continue playing smoothly even if the edge path is recalculated (for example when the source or target nodes are dragged.)