my/ui

Command Palette

Search for a command to run...

All components

Neon Border

borders

Container with a two-color neon glowing border using CSS filter drop-shadow and absolute positioned gradient layers. Vue v-bind CSS variables for dynamic color/duration replaced with inline React styles. Supports none/half/full animation width and configurable duration.

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/neon-border.json

Usage

"use client";

import { NeonBorder } from "@/registry/inspira-react/neon-border";

export default function NeonBorderDemo() {
  return (
    <div className="flex min-h-48 flex-col items-center justify-center gap-6 p-8">
      <NeonBorder color1="#0496ff" color2="#ff0a54" animationType="half">
        <div className="flex h-full w-full items-center justify-center rounded-lg bg-background px-4 py-2 text-sm font-medium">
          Neon Border — Half
        </div>
      </NeonBorder>

      <NeonBorder color1="#a855f7" color2="#ec4899" animationType="full" duration={4}>
        <div className="flex h-full w-full items-center justify-center rounded-lg bg-background px-4 py-2 text-sm font-medium">
          Neon Border — Full
        </div>
      </NeonBorder>

      <NeonBorder color1="#22c55e" color2="#f59e0b" animationType="none">
        <div className="flex h-full w-full items-center justify-center rounded-lg bg-background px-4 py-2 text-sm font-medium">
          Neon Border — Static
        </div>
      </NeonBorder>
    </div>
  );
}

Component source

"use client";

import { cn } from "@/lib/utils";
import type { ReactNode } from "react";

type AnimationType = "none" | "half" | "full";

interface NeonBorderProps {
  color1?: string;
  color2?: string;
  animationType?: AnimationType;
  duration?: number;
  className?: string;
  children?: ReactNode;
}

function getWidth(animationType: AnimationType): number {
  switch (animationType) {
    case "none":
      return 12;
    case "half":
      return 50;
    case "full":
      return 100;
  }
}

export function NeonBorder({
  color1 = "#0496ff",
  color2 = "#ff0a54",
  animationType = "half",
  duration = 6,
  className,
  children,
}: NeonBorderProps) {
  const animWidth = `${getWidth(animationType)}%`;
  const durationStr = `${duration}s`;
  const shouldAnimate = animationType !== "none";

  return (
    <div
      style={{ "--neon-border-duration": durationStr } as React.CSSProperties}
      className={cn(
        "relative z-10 inline-block h-10 w-full max-w-sm overflow-hidden rounded-lg p-px",
        className,
      )}
    >
      {/* Layer 1 - color1 side */}
      <div
        style={
          {
            position: "absolute",
            inset: 0,
            overflow: "hidden",
            width: "100%",
            height: "100%",
            filter: `blur(1px) drop-shadow(0 0 12px ${color1})`,
            zIndex: -1,
            borderRadius: "inherit",
            ...(shouldAnimate
              ? { animation: `neon-border-anim ${duration}s linear infinite` }
              : {}),
          } as React.CSSProperties
        }
      >
        <div
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            width: animWidth,
            height: "100%",
            background: `linear-gradient(135deg, ${color1}, ${color1}, transparent, transparent)`,
          }}
        />
      </div>

      {/* Layer 2 - color2 side */}
      <div
        style={
          {
            position: "absolute",
            inset: 0,
            overflow: "hidden",
            width: "100%",
            height: "100%",
            filter: `blur(1px) drop-shadow(0 0 12px ${color2})`,
            zIndex: -1,
            borderRadius: "inherit",
            ...(shouldAnimate
              ? { animation: `neon-border-anim ${duration}s linear infinite` }
              : {}),
          } as React.CSSProperties
        }
      >
        <div
          style={{
            position: "absolute",
            bottom: 0,
            right: 0,
            width: animWidth,
            height: "100%",
            background: `linear-gradient(135deg, transparent, transparent, ${color2}, ${color2})`,
          }}
        />
      </div>

      {/* Border overlay */}
      <div
        style={{
          position: "absolute",
          inset: 0,
          borderRadius: "inherit",
          border: `1px solid transparent`,
          background: `linear-gradient(135deg, ${color1}, ${color2}) border-box`,
          WebkitMask: "linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)",
          WebkitMaskComposite: "destination-out",
          maskComposite: "exclude",
        }}
      />

      {children}
    </div>
  );
}