A customizable animated beam that travels along the border of a container for a dynamic visual highlight.
import BorderBeam from "@/registry/visual-effects/border-beam/border-beam";
const BorderBeamPreview = () => {
return (
<div className="relative w-80 rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
<BorderBeam
colors={[
"#84fab0",
"#8fd3f4",
"#0000",
]}
width={2}
size={200}
/>
<BorderBeam
colors={[
"#84fab0",
"#8fd3f4",
"#0000",
]}
width={2}
size={200}
offset={50}
/>
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-blue-100 text-2xl dark:bg-blue-500/10">
✨
</div>
<h3 className="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
Border Beam
</h3>
<p className="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-400">
A smooth animated beam that continuously travels around the border,
creating a subtle yet eye-catching highlight for cards and UI elements.
</p>
<div className="mt-6 flex items-center justify-between">
<span className="rounded-full bg-zinc-100 px-3 py-1 text-xs font-medium text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
Interactive UI
</span>
<button className="rounded-lg bg-zinc-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-800 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200">
Explore
</button>
</div>
</div>
)
}
export default BorderBeamPreview; Install the following packages before using this component.
import { memo, useMemo } from "react";
import { motion, type Easing } from "motion/react";
import cn from "@/utils/cn";
export type BorderBeamProps = {
size?: number;
width?: number;
colors?: string[];
duration?: number;
offset?: number;
reverse?: boolean;
timingFn?: Easing | Easing[];
className?: string;
style?: React.CSSProperties,
} & React.ComponentProps<"span">;
export const BorderBeam = (
props: BorderBeamProps
) => {
const {
size = 50,
width = 1,
colors = [
"rgba(0, 0, 0, 0)",
"rgba(127, 127, 127, 1)",
"rgba(0, 0, 0, 0)"
],
duration = 5,
offset = 0,
reverse = false,
timingFn = "linear",
className,
style,
...restProps
} = props;
const _size = useMemo(() => Math.max(0, size), []);
const _width = useMemo(() => Math.max(0, width), []);
const _duration = useMemo(() => Math.max(0, duration), []);
const _offset = useMemo(() => Math.min(100, Math.max(0, offset)), []);
const gradient = useMemo(() => (
`linear-gradient(to left, ${colors.join()})`
), [colors]);
const variants = {
start: {
offsetDistance: `${_offset}%`
},
end: {
offsetDistance: `${_offset + 100}%`
}
};
return (
<span
{...restProps}
aria-hidden="true"
className={cn(
"absolute border border-[#0000] rounded-[inherit] pointer-events-none",
"[inset:var(--beam-width)]",
"[border-width:var(--beam-border-width)]",
"[mask-clip:padding-box,border-box]",
"[mask-composite:intersect]",
"[mask-image:linear-gradient(#0000,#0000),linear-gradient(#000,#000)]",
className,
)}
style={{
...style,
"--size": `${_size}px`,
"--beam-width": `${_width * -1}px`,
"--beam-border-width": `${_width}px`,
"--gradient": gradient,
} as React.CSSProperties}
>
<motion.span
className={cn(
"absolute inset-0 z-[2] block pointer-events-none",
"[width:var(--size)]",
"[height:var(--size)]",
"[background:var(--gradient)]",
"[offset-path:rect(0px_auto_auto_0px_round_var(--size))]",
)}
variants={variants}
animate={reverse ? "start" : "end"}
style={variants[reverse ? "end" : "start"]}
transition={{
ease: timingFn,
duration: _duration,
repeat: Infinity,
}}
/>
</span>
);
};
export default memo(BorderBeam); | Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| size | number | No | 50 | Size of the animated beam effect |
| width | number | No | 1 | Thickness of the beam |
| colors | string[] | No | ["rgba(0,0,0,0)", "rgba(127,127,127,1)", "rgba(0,0,0,0)"] | Gradient colors used to render the beam effect |
| duration | number | No | 5 | Duration of the animation in seconds |
| offset | number | No | 0 | Offset position where the animation starts |
| reverse | boolean | No | false | Reverses the direction of the beam animation |
| timingFn | string | No | "linear" | Motion animation timing function (e.g., ease, linear, easeInOut) |
| className | string | No | — | Additional CSS class names applied to the component |
If you find this component useful, consider starring the repository on GitHub. Found a bug or have a suggestion? Open an issue to help improve it.