Skip to content

Spinner

Shows that something is loading.

Library
What’s this?

Installation

pnpm dlx shadcn@latest add @wisty/spinner

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • Something is loading or running and you can't show its shape, like a search, a save or a refresh.
  • A button starts a slow action and should show it's busy.

Don't use it

  • You know the shape of the content that's loading, like a list or a card. Use Skeleton.
  • You know how far along a task is. Use Progress.
  • The wait is under about 300ms. Show nothing.

Usage

Render a Spinner where the content will appear and name it for what's loading. Inside a Button it takes the button's text color: disable the button and keep its label next to the spinner.

import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
<Button disabled>
  <Spinner aria-label="Saving" />
  Saving
</Button>

Examples

Sizes

SLDS's xx-small, x-small, small, medium and large spinners, set with `size-3`, `size-4` (the default), `size-5`, `size-8` and `size-12`.

Colors

The brand spinner with `text-primary`, and the inverse spinner on a dark surface with `text-inverse-foreground`.

API reference

Spinner

Also takes every <svg> (opens in a new tab) prop.

SLDS mapping

Spinner is slds-spinner slds-spinner_inline: six dots that bounce outward in turn. Sizes follow SLDS's widths: size-3 is slds-spinner_xx-small, size-4 is _x-small, size-5 is _small, size-8 is _medium and size-12 is _large, as the Sizes example shows. className="text-primary" is slds-spinner_brand, and text-inverse-foreground is slds-spinner_inverse, as the Colors example shows.

Known gaps:

  • SLDS's spinner container (slds-spinner_container), the overlay that covers a region while it loads, isn't part of the spinner. Build it with a positioned div if you need it.
  • SLDS's delayed spinner (slds-spinner_delayed) isn't a prop. Render the spinner after 300ms instead.
  • SLDS's inline spinner centers its dots on the top edge of its box. This one centers them in the box.
  • In Lightning Blue, text-primary is a little darker than SLDS's brand spinner.

Accessibility

  • The spinner is a role="status" named "Loading". Name it for what's loading with aria-label, like "Loading contacts".
  • Inside a button, keep the button's own text, like "Saving". It names the action; the spinner's label names the wait.

Motion

  • The dots bounce outward in turn, once a second, in every theme.
  • With reduced motion the dots hold still. The spinner's name still says something is loading.

Responsive behavior

  • The spinner keeps its size in a flex row. Set it with size-* classes, not width and height.
  • The dots spill out of the box. Together they're about a quarter wider than it at rest, and half again as wide at the top of a bounce. Keep that much space free around it, more so for large spinners.

Rules

  • Use only the gray default, text-primary for the brand spinner and text-inverse-foreground on dark surfaces.
  • Don't add animate-spin. The spinner has its own motion.