---
title: Beam
description: Soft edge light that travels around an outline on its own.
group: Path
order: 11
new: true
---

A traveling highlight on the outline of a surface. Use it on cards, outline buttons, and outline badges. Tune the colors and duration.

## Cards

```tsx
import { Beam } from "@/components/motion/beam"
import { Button } from "@/components/ui/button"

export function Example() {
  return (
    <Beam hoverFull>
      <Button variant="outline" className="border-0 bg-transparent shadow-none">
        Continue
      </Button>
    </Beam>
  )
}
```

## Buttons

Outline only. Hover lights the full outline.

```tsx
import { Beam } from "@/components/motion/beam"
import { Button } from "@/components/ui/button"

export function Example() {
  return (
    <Beam hoverFull>
      <Button variant="outline" className="border-0 bg-transparent shadow-none">
        Continue
      </Button>
    </Beam>
  )
}
```

## Badges

Outline only.

```tsx
import { Beam } from "@/components/motion/beam"
import { Button } from "@/components/ui/button"

export function Example() {
  return (
    <Beam hoverFull>
      <Button variant="outline" className="border-0 bg-transparent shadow-none">
        Continue
      </Button>
    </Beam>
  )
}
```

## Grid

A bento layout where the outline lights up under the pointer. The glow reaches past each tile so neighbors catch it across the gaps.

```tsx
import { Beam } from "@/components/motion/beam"
import { Button } from "@/components/ui/button"

export function Example() {
  return (
    <Beam hoverFull>
      <Button variant="outline" className="border-0 bg-transparent shadow-none">
        Continue
      </Button>
    </Beam>
  )
}
```

## Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add beam
```

## Usage

```tsx
import { Beam } from "@/components/motion/beam"
import { Button } from "@/components/ui/button"
```

```tsx
<Beam hoverFull>
  <Button variant="outline" className="border-0 bg-transparent shadow-none">
    Continue
  </Button>
</Beam>
```

## API Reference

### Beam

| Prop | Type | Default |
| --- | --- | --- |
| colors | [string, string, string] | ["#78c8ff", "#be8cff", "#ffb48c"] |
| duration | number | 8 |
| size | number | 28 |
| borderWidth | number | 1 |
| hoverFull | boolean | false |
| className | string | — |
| children | ReactNode | — |

Also accepts the other native `div` attributes. `size` is the lit arc length as a percent of the outline (clamped 10-80).

## Source

```tsx
"use client";

import "@/lib/motion-styles";

import {
  useState,
  type ComponentProps,
  type CSSProperties,
  type ReactNode,
} from "react";
import { cn } from "@/lib/utils";

export type BeamColors = [string, string, string];

export type BeamProps = ComponentProps<"div"> & {
  colors?: BeamColors;
  /** Seconds for one full loop around the edge. */
  duration?: number;
  /** Arc the highlight covers at rest, as a share of the path (10–80). */
  size?: number;
  /** Outline thickness in px. */
  borderWidth?: number;
  /** On hover, grow the beam until it covers the full outline. */
  hoverFull?: boolean;
  children?: ReactNode;
};

const DEFAULT_COLORS = ["#78c8ff", "#be8cff", "#ffb48c"] as const;

/** Overruns the full path so the seam at the start angle stays lit. */
const FULL_SPAN = 280;

function Beam({
  className,
  colors = [...DEFAULT_COLORS],
  duration = 8,
  size = 28,
  borderWidth = 1,
  hoverFull = false,
  children,
  style,
  onMouseEnter,
  onMouseLeave,
  ...props
}: BeamProps) {
  const [c1, c2, c3] = colors;
  const [hovered, setHovered] = useState(false);
  const restingSpan = Math.min(Math.max(size, 10), 80);
  const span = hoverFull && hovered ? FULL_SPAN : restingSpan;

  return (
    <div
      data-slot="beam"
      className={cn(
        "arctis-beam relative rounded-md p-[var(--beam-border)]",
        className,
      )}
      style={
        {
          "--beam-1": c1,
          "--beam-2": c2,
          "--beam-3": c3,
          "--beam-duration": `${Math.max(duration, 0.5)}s`,
          "--beam-border": `${borderWidth}px`,
          "--beam-span": `${span}%`,
          background: "var(--border)",
          ...style,
        } as CSSProperties
      }
      onMouseEnter={(event) => {
        onMouseEnter?.(event);
        if (hoverFull) setHovered(true);
      }}
      onMouseLeave={(event) => {
        onMouseLeave?.(event);
        if (hoverFull) setHovered(false);
      }}
      {...props}
    >
      <span aria-hidden="true" className="arctis-beam-glow" />
      <div className="relative z-10 size-full overflow-hidden rounded-[inherit] bg-background">
        {children}
      </div>
    </div>
  );
}

export { Beam, DEFAULT_COLORS as BEAM_DEFAULT_COLORS };
```
