Border beam button

Free · MIT

A short light beam rides around the button's rounded border with CSS offset-path, with a conic fallback and a static gradient for reduced motion.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/border-beam-button.json

The file lands in components/goodui/border-beam-button.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/border-beam-button works too.

Usage

import { PlayCircle } from "lucide-react"
import { BorderBeamButton } from "@/components/goodui/border-beam-button"

export function DemoCta({ onWatch }: { onWatch: () => void }) {
  return (
    <BorderBeamButton size="lg" radius={12} onClick={onWatch}>
      <PlayCircle />
      Watch the 2-minute demo
    </BorderBeamButton>
  )
}

When to use

Use it for
  • A secondary CTA that should feel alive without the loudness of a rainbow edge ("Watch the 2-minute demo").
  • Highlighting one new action in an app header or empty state.
  • Pairing with a solid primary button: the outline variant with a beam draws the eye to the second option.
Reach for something else
  • A beam around a card, input or any other element: use border-beam, which wraps any child and measures its radius.
  • A full multicolor flowing border: use rainbow-button.
  • Ordinary actions in forms and tables: use button; a moving border on every action is noise.
If you needUse
Secondary CTA next to a primary onevariant="outline" (default)
Primary CTA with a beamvariant="solid"
Pill shaperadius={999}
Two beams chasing each otherdouble
Brand colorscolorFrom="var(--chart-1)" colorTo="var(--chart-4)"

Examples

Solid pill with two beams in brand colors

<BorderBeamButton
  variant="solid"
  radius={999}
  double
  colorFrom="var(--chart-1)"
  colorTo="var(--chart-4)"
  duration={6}
>
  Start free trial
</BorderBeamButton>

API reference

PropTypeDefault
variant
"outline" is a page-colored face with a hairline border; "solid" fills with the primary color.
"outline" | "solid""outline"
size
"sm" | "md" | "lg": height and padding of the button.
"sm" | "md" | "lg""md"
radius
Corner radius in px (use 999 for a pill). Kept in sync with the beam path.
number10
colorFrom
Head color of the beam.
string"#ffaa40"
colorTo
Tail color of the beam.
string"#9c40ff"
duration
Seconds per lap.
number4
beamSize
Beam length in px.
number56
borderWidth
Border thickness in px.
number1.5
double
Two beams chasing each other half a lap apart.
boolean—

Also accepts every <button> attribute.

Accessibility

Enter / SpaceActivates the button (native <button>).
TabMoves focus; a focus ring shows on keyboard focus only.
  • Renders a native <button>; the beam ring is an aria-hidden span with pointer-events: none.
  • The label is never covered: the beam is masked to the border band only.

Motion

  • A short gradient beam (`beamSize`, 56 px by default) travels the rounded-rect edge with CSS offset-path in a linear loop, one lap every `duration` seconds (4 by default). double adds a second beam half a lap behind.
  • Browsers without offset-path rect() get a rotating conic sweep masked to the same border band instead.
  • Press scales to 0.98.
  • Reduced motion: the beam is hidden and the border shows a static from-to gradient at 55% opacity.
  • CSS only; no JavaScript runs while the beam moves.

Notes for AI

Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.

  • Set the corner radius with the radius prop (px), not rounded-* classes: the beam path uses the same value.
  • duration is seconds per lap; beamSize and borderWidth are px.
  • variant is "outline" or "solid" only.
  • For anything that is not a button (cards, inputs), use border-beam instead of this component.

FAQ

Why does the beam cut the corner on my button?

The beam path uses the radius prop. If you round the button with a class instead, the two no longer match. Pass radius={…} (999 for a pill).

What do older browsers show?

Without offset-path rect() support, a rotating conic gradient sweeps around the same border band instead of the traveling beam.

Related