Spinner
A spinning loading indicator for indeterminate async operations. Built as an SVG component using Loader2Icon from lucide-react with animate-spin, role="status", and a built-in accessible label.
Anatomy
A single <Spinner> SVG element with role="status" and aria-label="Loading". Default size is size-4. Override via className.
role="status"aria-label="Loading"animate-spinsize-4 default
Sizes
Override size via a Tailwind size-* className. Use the smallest size that remains legible in context.
size-4Inline, buttons
size-6Cards, panels
size-8Full-page overlays
In Context
The most common usage patterns — loading buttons and inline text indicators.
Loading button
Inline text
Syncing changes…
Generating report…
Centered in container
Muted color
Background task running
Design Guidelines
Do
- Use for unknown durations. Spinner is best when you can't measure completion — network requests, AI generation, async operations.
- Pair with disabled state on triggers. Disable the button or form that initiated the action while the spinner is visible.
- Add a label in context. "Saving…" or "Loading" beside the spinner prevents ambiguity.
Don't
- Don't use when progress is quantifiable. If you know the percentage, use Progress instead — it gives users more useful feedback.
- Don't show spinner for instant operations. Under ~200ms, a spinner causes more confusion than it prevents.
- Don't spin indefinitely without a timeout. After a reasonable threshold, show an error state with a retry option.
Developer Reference
Accessibility
role="status"announces to screen readers that a loading state is active.- Built-in
aria-label="Loading"gives the SVG an accessible name without extra markup. - When used inside a button, pair with
disabledand add visible orsr-onlytext to describe the action (e.g., "Saving…"). - Animation uses
animate-spin— respectsprefers-reduced-motionvia Tailwind's motion utilities.
Usage
import { Spinner } from "@/components/ui/spinner"
// Default (size-4)
<Spinner />
// Custom sizes
<Spinner className="size-6" />
<Spinner className="size-8" />
// Loading button
<Button disabled>
<Spinner />
Saving…
</Button>
// Inline text
<p className="flex items-center gap-2">
<Spinner className="size-3.5" />
Syncing changes…
</p>
// Centered in a container
<div className="flex h-40 items-center justify-center">
<Spinner className="size-6" />
</div>