my/ui

Command Palette

Search for a command to run...

All components

Glow Border

borders

Animated radial-gradient rotating border overlay using injected @keyframes and useMemo-computed inline styles. Vue v-bind() CSS replaced with CSS custom properties on the element style.

responsive · 600px

Install

Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:

$npx shadcn@latest add https://your-domain/r/glow-border.json

Usage

"use client";

import { GlowBorder } from "@/registry/inspira-react/glow-border";

export default function GlowBorderDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-8 p-12 bg-neutral-950 min-h-[200px]">
      <div className="relative w-48 h-24 bg-neutral-900 rounded-[10px] flex items-center justify-center">
        <GlowBorder color="#06b6d4" borderRadius={10} />
        <span className="text-white text-sm font-medium">Cyan Glow</span>
      </div>
      <div className="relative w-48 h-24 bg-neutral-900 rounded-2xl flex items-center justify-center">
        <GlowBorder color={["#ff0080", "#7928ca", "#0070f3"]} borderRadius={16} duration={5} borderWidth={2} />
        <span className="text-white text-sm font-medium">Multi Color</span>
      </div>
      <div className="relative w-48 h-24 bg-neutral-900 rounded-full flex items-center justify-center">
        <GlowBorder color="#f59e0b" borderRadius={9999} borderWidth={3} duration={8} />
        <span className="text-white text-sm font-medium">Amber Pill</span>
      </div>
    </div>
  );
}

Component source

"use client";

import React, { useMemo } from "react";
import { cn } from "@/lib/utils";

interface GlowBorderProps {
  borderRadius?: number;
  color?: string | string[];
  borderWidth?: number;
  duration?: number;
  className?: string;
}

export function GlowBorder({
  borderRadius = 10,
  color = "#FFF",
  borderWidth = 2,
  duration = 10,
  className,
}: GlowBorderProps) {
  const colorValue = Array.isArray(color) ? color.join(",") : color;

  const styles: React.CSSProperties = useMemo(
    () => ({
      "--border-radius": `${borderRadius}px`,
      "--border-width": `${borderWidth}px`,
      "--duration": `${duration}s`,
      backgroundImage: `radial-gradient(transparent,transparent, ${colorValue},transparent,transparent)`,
      backgroundSize: "300% 300%",
      mask: "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
      WebkitMask: "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
      WebkitMaskComposite: "xor",
      maskComposite: "exclude",
      padding: "var(--border-width)",
      borderRadius: "var(--border-radius)",
      animation: `glow-border-spin var(--duration) linear infinite`,
    } as React.CSSProperties),
    [borderRadius, borderWidth, duration, colorValue],
  );

  return (
    <>
      <style>{`
        @keyframes glow-border-spin {
          0% { background-position: 0% 0%; }
          50% { background-position: 100% 100%; }
          100% { background-position: 0% 0%; }
        }
      `}</style>
      <div
        style={styles}
        className={cn(
          "pointer-events-none absolute inset-0 size-full rounded-[inherit] will-change-[background-position]",
          className,
        )}
      />
    </>
  );
}