Cognition

Components

Spinner

An indeterminate loading indicator for waits of unknown length. A brand arc rotates on a neutral track, spinning continuously until the wait ends.

Smooth by construction. The arc spins with a single linear rotation from 0 to 360 degrees, so the loop closes on itself with no seam or stutter. The arc maps to background-primary and the track to border-default, so both remap in dark mode.

Preview

Processing payment...

Rendered with live Cognition tokens. Toggle the theme and the arc and track remap, no dark: classes.

Variants

<Spinner size="sm" />
<Spinner />
<Spinner size="lg" />

Three sizes at 16, 24, and 32 pixels. Stroke weight scales with the diameter.

States

Spinning. The single, perpetual state until the wait resolves.

Saving changes...

Inline beside a label.

Inside a disabled button.

The spinner is indeterminate, so it has one continuous state. Pair it with text or a control to signal what is loading.

API

Prop
Type
Default
Description
size
"sm" | "default" | "lg"
"default"
Diameter: 16, 24, or 32 pixels.
label
string
"Loading"
Accessible label announced to screen readers via role=status.
className
string
undefined
Layout or size overrides on the wrapper.

Sets role="status" with an aria-label so the wait is announced.

Don't and Do

Don't

Don't reach for a spinner when the final layout is already known. A spinner leaves the reader staring at a blank region and then jolts them with a content shift. Where the shape of the result is predictable, a Skeleton holds the space and reads as calmer.

Do
<Spinner />
<Spinner size="sm" />   // inline, e.g. in a button
<Spinner size="lg" />

Use it for waits of unknown length where there is no layout to preview, such as a submit in flight or a background task finishing.

import { Spinner } from "@/components/ui/spinner";

export function SavingState() {
  return (
    <div className="flex items-center gap-2 text-sm text-text-subtle">
      <Spinner size="sm" />
      Saving changes...
    </div>
  );
}