Skip to main content

Overview

The moving-border.tsx file exports two components:
  1. Button - A button wrapper with an animated moving border effect
  2. MovingBorder - The core animation component that moves an element along an SVG path
The effect creates a glowing element that travels continuously around the border of a container, creating a futuristic, attention-grabbing visual.

Source Location

/workspace/source/src/app/components/ui/moving-border.tsx

Button Component

Overview

A pre-built button component with an animated border effect. The border features a moving radial gradient that travels around the button’s perimeter.

Props

borderRadius
string
default:"1.75rem"
Border radius for the button container
children
React.ReactNode
required
Content to display inside the button
as
any
default:"button"
Component type to render as (e.g., “button”, “a”, Link, etc.)
containerClassName
string
Additional classes for the outer button container
borderClassName
string
Additional classes for the moving gradient element
duration
number
Animation duration in milliseconds. Passed to MovingBorder component.
className
string
Additional classes for the inner content wrapper
...otherProps
any
All other props are spread to the root component element

Type Definition

From /workspace/source/src/app/components/ui/moving-border.tsx:13-31:
export function Button({
  borderRadius = "1.75rem",
  children,
  as: Component = "button",
  containerClassName,
  borderClassName,
  duration,
  className,
  ...otherProps
}: {
  borderRadius?: string;
  children: React.ReactNode;
  as?: any;
  containerClassName?: string;
  borderClassName?: string;
  duration?: number;
  className?: string;
  [key: string]: any;
})

Default Styling

Container

"bg-transparent relative text-xl h-16 w-40 p-[1px] overflow-hidden"

Border Element

"h-20 w-20 opacity-[0.8] bg-[radial-gradient(var(--sky-500)_40%,transparent_60%)]"

Inner Content

"relative bg-slate-900/[0.8] border border-slate-800 backdrop-blur-xl text-white flex items-center justify-center w-full h-full text-sm antialiased"

Usage Example

import { Button } from "@/components/ui/moving-border";

export default function CallToAction() {
  return (
    <Button
      borderRadius="1.75rem"
      className="bg-slate-900 text-white border-slate-800"
      duration={3000}
    >
      Get Started
    </Button>
  );
}

As Different Element

import Link from "next/link";
import { Button } from "@/components/ui/moving-border";

export default function NavLink() {
  return (
    <Button
      as={Link}
      href="/projects"
      borderRadius="2rem"
      className="px-8 py-4"
    >
      View Projects
    </Button>
  );
}

MovingBorder Component

Overview

The core animation component that moves children along an SVG path. This is a lower-level component used by the Button component but can be used independently for custom implementations.

Props

children
React.ReactNode
required
Element to animate along the path (typically a gradient or glow effect)
duration
number
default:2000
Time in milliseconds for one complete loop around the path
rx
string
Horizontal corner radius for the SVG rectangle path
ry
string
Vertical corner radius for the SVG rectangle path
...otherProps
any
Additional props spread to the SVG element

Type Definition

From /workspace/source/src/app/components/ui/moving-border.tsx:72-84:
export const MovingBorder = ({
  children,
  duration = 2000,
  rx,
  ry,
  ...otherProps
}: {
  children: React.ReactNode;
  duration?: number;
  rx?: string;
  ry?: string;
  [key: string]: any;
})

Animation Implementation

The animation uses Framer Motion’s useAnimationFrame hook to create smooth, continuous movement along an SVG path.

Key Hooks Used

From /workspace/source/src/app/components/ui/moving-border.tsx:85-105:
const pathRef = useRef<any>();
const progress = useMotionValue<number>(0);

useAnimationFrame((time) => {
  const length = pathRef.current?.getTotalLength();
  if (length) {
    const pxPerMillisecond = length / duration;
    progress.set((time * pxPerMillisecond) % length);
  }
});

const x = useTransform(
  progress,
  (val) => pathRef.current?.getPointAtLength(val).x
);
const y = useTransform(
  progress,
  (val) => pathRef.current?.getPointAtLength(val).y
);

const transform = useMotionTemplate`translateX(${x}px) translateY(${y}px) translateX(-50%) translateY(-50%)`;
How it works:
  1. useAnimationFrame runs on every frame
  2. Calculates current position based on elapsed time
  3. getTotalLength() gets the SVG path’s total length
  4. getPointAtLength() gets x,y coordinates at the current position
  5. Transform template positions the child element at those coordinates

SVG Path Structure

From /workspace/source/src/app/components/ui/moving-border.tsx:108-125:
<svg
  xmlns="http://www.w3.org/2000/svg"
  preserveAspectRatio="none"
  className="absolute h-full w-full"
  width="100%"
  height="100%"
  {...otherProps}
>
  <rect
    fill="none"
    width="100%"
    height="100%"
    rx={rx}
    ry={ry}
    ref={pathRef}
  />
</svg>
The rect element with fill="none" creates an invisible path that defines the border shape. The rx and ry props control corner rounding.

Custom Usage Example

import { MovingBorder } from "@/components/ui/moving-border";

export default function CustomCard() {
  return (
    <div className="relative w-64 h-64 p-[2px] rounded-xl overflow-hidden">
      <div className="absolute inset-0">
        <MovingBorder duration={3000} rx="12" ry="12">
          <div className="h-24 w-24 bg-gradient-to-r from-pink-500 to-purple-500 blur-xl" />
        </MovingBorder>
      </div>
      
      <div className="relative z-10 bg-black rounded-xl h-full w-full p-6">
        <h3>Custom Card</h3>
        <p>With moving gradient border</p>
      </div>
    </div>
  );
}

Performance Notes

The animation uses useAnimationFrame which runs on every frame. For optimal performance:
  • Keep the animated element small (typically 20-100px)
  • Use GPU-accelerated properties (transform, opacity)
  • Limit the number of simultaneous moving borders on a page

Dependencies

  • framer-motion - Required hooks:
    • motion - For animated div
    • useAnimationFrame - For continuous animation
    • useMotionTemplate - For transform template string
    • useMotionValue - For animation progress value
    • useTransform - For coordinate transformations
  • @/lib/utils - Provides cn() class merging utility
  • React (useRef)

Browser Compatibility

The component uses SVG getTotalLength() and getPointAtLength() methods, which are supported in all modern browsers. For older browsers, consider a polyfill.

Customization Examples

Faster Animation

<Button duration={1000}>
  Fast Border
</Button>

Custom Border Color

<Button
  borderClassName="bg-[radial-gradient(#ff0080_40%,transparent_60%)]"
>
  Pink Border
</Button>

Different Shape

<Button borderRadius="0.5rem">
  Rounded Rectangle
</Button>

Large Button

<Button
  containerClassName="h-20 w-64 text-2xl"
  className="text-lg"
>
  Large CTA
</Button>

Build docs developers (and LLMs) love