Spinner
A circular animated indicator that signals an ongoing process with no known completion time.
Plain English
A spinner reassures users that something is happening even when they cannot see progress. Unlike a progress bar, a spinner has no end point — it just communicates "working." Spinners should appear within 100ms of an action triggering a wait. Overusing them erodes trust: if every interaction spins, nothing feels fast. Reserve spinners for genuine unknowns like API calls, not for short operations under 300ms.
Technical
CSS technique — border: 3px solid #e2e8f0; border-top-color: #3b82f6; border-radius: 50%; animation: spin 0.8s linear infinite; @keyframes spin { to { transform: rotate(360deg); } }. Common sizes: 16px (inline text), 24px (inside buttons), 40px (full-area loaders). Use the brand primary color for the active arc; keep the track in a light neutral.
Live Demo
Spinner variants
Circular border
Bouncing dots
Pulsing bars
Large ring
Spinners signal activity — use when duration is unknown; prefer skeletons for content areas.
Usage
✓ Good usage
A 24px spinner appearing inside a "Save" button while a form submits, replacing the label with "Saving…" — confirming the action is processing.
✗ Bad usage
A full-page spinner for a dropdown that loads 5 options in 80ms — the flash of spinner is more jarring than the perceived wait.
Recommended values
- Size: 16px (inline), 24px (button), 40px (page-level)
- Speed: 0.6–0.9s linear infinite
- Active arc: brand primary color
- Track: light neutral (#e2e8f0)
- Appear after: 100–150ms delay
- Border width: 2–4px depending on size
Common mistakes
- No delay before showing — spinner flashes briefly on fast operations, which looks broken.
- Spinner with no accessible label — screen readers see nothing without aria-label="Loading".
- Too large on mobile — a 60px spinner feels overwhelming in a compact list.
AI Prompt Example
Copy this into Claude, Cursor, Bolt, or v0.
Replace loading states in this component with accessible spinners. Show a 24px circular spinner inside buttons during async operations, a 40px spinner centered in the content area for page-level loads. Always pair with aria-label="Loading" and appear after a 100ms delay.