Spinner

An inline loading indicator for asynchronous operations. Uses a CSS animation so it works without JavaScript and never blocks the main thread.

Preview

Loading…Loading…Loading…Loading…

Sizes

SizeDiameterStroke widthTypical use
xs12px3pxInside buttons or badges
sm16px2.5pxInline text, compact rows
md24px2.5pxDefault — cards, panels
lg32px2pxFull-section loading states

Inline usage

Saving changes…Saving changes…
Loading…Syncing

Implementation

Spinner.tsx
tsx
import { Spinner } from "@/components/ui/Spinner";

// Default
<Spinner />

// Sizes
<Spinner size="xs" />
<Spinner size="sm" />
<Spinner size="md" />
<Spinner size="lg" />

// Custom accessible label
<Spinner label="Saving changes…" />

// Inline within text
<div className="flex items-center gap-2">
  <Spinner size="sm" />
  <span>Loading results…</span>
</div>

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md" | "lg""md"Controls the spinner diameter and stroke width.
labelstring"Loading…"Accessible label announced by screen readers via aria-label and a visually-hidden <span>.
classNamestringAdditional classes applied to the outer <span> wrapper.

Accessibility

  • The wrapper uses role="status" so screen readers announce when loading begins.
  • The SVG is aria-hidden; the accessible text is provided by a visually-hidden <span> — both the aria-label and the sr-only text are used for maximum compatibility.
  • Provide a descriptive label prop when the context is not obvious (e.g. "Saving changes…" vs the default "Loading…").
  • For page-level loading states, consider aria-live="polite" on the container so users are notified when content becomes available.
  • Do not use Spinner as the sole loading indicator inside a button that already handles its own loading state — prefer the Button component's built-in loading prop.