Animated Beam
A flowing beam between nodes, well suited to integration diagrams and data-flow animations.
Draws a flowing beam between any two nodes, with the path recalculated automatically as the container resizes — ideal for integration diagrams, data pipelines, and architecture diagrams.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add animated-beam.enAnimated Beam is a React animated connector beam component built with SVG and Motion (motion/react). It draws a straight line or quadratic Bézier curve between the measured positions of two nodes, then sends a gradient highlight flowing along it. Typical uses are integration diagrams, data-flow illustrations and "many sources into one platform" visuals.
Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<AnimatedBeam/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
When to use
- An integrations section on a SaaS homepage, connecting third-party services to your product logo
- Architecture diagrams showing data flow, for example user, API and database
- Developer tool pages explaining sync or deploy flows, with reversed beams for two-way traffic
- A "how it works" illustration on a feature page
- Not a good fit: for ordered steps with text labels, use a labelled component such as Orbit Timeline or Stepper
Installation
npx webberui@latest add https://webberui.com/r/animated-beam.en.jsonOr, once registries are configured in components.json, install it as @webberui/animated-beam.
Usage
The beam is an absolutely positioned overlay that measures coordinates through three refs: containerRef is the measurement origin, and fromRef / toRef are the nodes at each end. Just put the nodes and the beam inside the same relative container.
"use client";
import * as React from "react";
import { AnimatedBeam } from "@/components/ui/animated-beam";
export function Diagram() {
const containerRef = React.useRef<HTMLDivElement>(null);
const fromRef = React.useRef<HTMLDivElement>(null);
const toRef = React.useRef<HTMLDivElement>(null);
return (
<div ref={containerRef} className="relative flex justify-between p-10">
<div ref={fromRef} className="size-11 rounded-full bg-white" />
<div ref={toRef} className="size-11 rounded-full bg-white" />
<AnimatedBeam
containerRef={containerRef}
fromRef={fromRef}
toRef={toRef}
/>
</div>
);
}The nodes need real dimensions and positioning; the beam is layered beneath them with -z-10, so make sure the container carries relative. Use different delay values to offset the flow timing of multiple beams, and curvature to spread apart lines that converge on the same hub.
Examples
Several sources into one hub
Each node on the left gets its own beam to the central node. Different curvature values fan the lines out, and delay staggers the flow.
"use client";
import * as React from "react";
import { AnimatedBeam } from "@/components/ui/animated-beam";
const sources = ["Forms", "Payments", "Support"];
export function IntegrationDiagram() {
const containerRef = React.useRef<HTMLDivElement>(null);
const hubRef = React.useRef<HTMLDivElement>(null);
const sourceRefs = [
React.useRef<HTMLDivElement>(null),
React.useRef<HTMLDivElement>(null),
React.useRef<HTMLDivElement>(null),
];
return (
<div ref={containerRef} className="relative isolate flex items-center justify-between p-10">
<div className="flex flex-col gap-8">
{sources.map((label, i) => (
<div key={label} ref={sourceRefs[i]} className="rounded-lg border bg-white px-3 py-2 text-sm">
{label}
</div>
))}
</div>
<div ref={hubRef} className="size-14 rounded-full border bg-white" />
{sourceRefs.map((ref, i) => (
<AnimatedBeam
key={i}
containerRef={containerRef}
fromRef={ref}
toRef={hubRef}
curvature={(1 - i) * 40}
delay={i * 0.4}
/>
))}
</div>
);
}Two-way traffic
Put two beams between the same nodes, one forward and one reverse, and separate them with Y offsets so they do not overlap.
<AnimatedBeam
containerRef={containerRef}
fromRef={appRef}
toRef={serverRef}
startYOffset={-8}
endYOffset={-8}
curvature={20}
/>
<AnimatedBeam
containerRef={containerRef}
fromRef={appRef}
toRef={serverRef}
startYOffset={8}
endYOffset={8}
curvature={-20}
reverse
/>Custom colors and width
gradientStartColor and gradientStopColor set the flowing highlight's gradient, while pathColor and pathOpacity set the static track underneath.
<AnimatedBeam
containerRef={containerRef}
fromRef={fromRef}
toRef={toRef}
pathWidth={3}
pathColor="#a3a3a3"
pathOpacity={0.3}
gradientStartColor="#f97316"
gradientStopColor="#facc15"
duration={4}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
containerRef | RefObject<HTMLElement | null> | — | The outer container used to measure coordinates |
fromRef | RefObject<HTMLElement | null> | — | The node the beam starts at |
toRef | RefObject<HTMLElement | null> | — | The node the beam ends at |
curvature | number | 0 | Curvature (px): positive arcs upward, negative dips downward |
reverse | boolean | false | Flow in reverse (end toward start) |
duration | number | 3 | Duration of one pass of the flow (seconds) |
delay | number | 0 | Delay before the reveal starts (seconds) |
pathColor | string | "currentColor" | Color of the static base line |
pathWidth | number | 2 | Line width (px) |
pathOpacity | number | 0.15 | Opacity of the base line |
gradientStartColor | string | "#818cf8" | Start color of the flowing gradient |
gradientStopColor | string | "#22d3ee" | End color of the flowing gradient |
startXOffset | number | 0 | Fine X adjustment for the start point (px) |
startYOffset | number | 0 | Fine Y adjustment for the start point (px) |
endXOffset | number | 0 | Fine X adjustment for the end point (px) |
endYOffset | number | 0 | Fine Y adjustment for the end point (px) |
Accessibility
- When the user has "reduce motion" enabled at the system level, only the static base line remains and the flowing highlight is not played
- The beam layer carries
aria-hidden; it is decorative only, so it does not interfere with assistive technology - The path watches the container's size through a
ResizeObserverand recalculates automatically in responsive layouts, cleaning up on unmount
FAQ
Does it work with the Next.js App Router?
Yes. The file already starts with "use client", but since you need to create refs, the component that wraps the nodes and beams must be a client component too, as in the "use client" at the top of the examples above. The path is measured in an effect, so the server renders an empty path.
Why can I not see the beam?
Check that the container has relative and the nodes have real dimensions. The beam sits under the nodes with -z-10, so a container with its own background color can hide it; add isolate to the container to create a new stacking context.
Does the path update when the layout changes?
It recomputes through a ResizeObserver when the container resizes. Only the container is observed, so if a node moves while the container size stays the same (for example a node with its own motion), the path does not follow.
Does it respect reduced motion?
Yes. With the OS "reduce motion" setting on, only the static track is drawn and the flowing highlight is skipped. The whole SVG is aria-hidden, so screen readers ignore it.
What are the dependencies?
motion, clsx and tailwind-merge. You can also copy the source from the Manual tab in the installation section.