Docs
Spinner

Spinner

Indeterminate loading indicator — an operation in progress with no predictable duration (initial loading, network request). For known progress, use ProgressBar.

When to use

✅ Use when…🚫 Avoid when…
  • During short API requests where the duration cannot be estimated.
  • In buttons/inputs that trigger asynchronous actions (isLoading).
  • In "loading…" states before the content's first render.
  • When you can estimate the duration. Use ProgressBar.
  • As permanent decoration. A never-ending spinner confuses the user.

Size

The size prop accepts a number (px) or a CSS string. Without a value, it uses 1em — inheriting from the context's font-size.

<Spinner size={50} />

Color

Spinner supports all theme colors via color (maps to the SVG's stroke).

<Spinner color="primary" />
<Spinner color="warning" size={32} />

Countdown ring

variant="countdown" is a determinate ring: the arc drains over duration (ms) and refills whenever cycle changes — pass the last poll timestamp or a counter. It announces as role="timer", not as a loading status.

<Spinner variant="countdown" duration={15000} cycle={lastPollAt} color="primary" />

Use it for "next refresh in…" next to polled data. For task progress with a known percentage, use ProgressBar.

Composing with IconButton/Notification

Since the size inherits from font-size, the spinner fits naturally into any slot that accepts an Icon.

<IconButton icon={<Spinner />} size="small" />
 
<Notification
  type="block"
  variant="primary"
  title="please wait…"
  message="We are processing your request"
  icon={<Spinner />}
/>

Accessibility

role="status" with the accessible name coming from label (default 'Carregando'). Use label to say what is loading when there is more than one region on the screen.

Keep only one Spinner announcing per loading region — several spinners firing role="status" at once turn into noise for screen reader users.

<Spinner label="Loading statement" />

When the spinner is decorative — there is already a visible loading text next to it — hide it to avoid duplicating the announcement:

<Flex gap="small" alignItems="center">
  <Spinner aria-hidden />
  <Text text="Loading statement…" />
</Flex>

Properties

Prop
Type
Default
Description
color
string
Theme token (`theme.colors`) used as `color`. Has lower precedence than `contrast`: when both are provided, the contrast calculation wins. Stays `string` (not the typed union): `color` is also an HTML attribute (`<li color>`, etc.) and interfaces extending native props require identical types on the name collision.
containment
string
Containment marker for the `cq` channel: emits `container-type` (+ `container-name` when the string form carries one, e.g. `'inline-size card'`). Apply on the immediate wrapper of the adapting component — NEVER on page-level shells.
cycle
string | number
Restart key for the countdown. Any change (a poll timestamp, a counter) refills the ring and starts draining again. Ignored by the indeterminate variant.
duration
number
15000
Countdown length in milliseconds. Ignored by the indeterminate variant.
label
string
Carregando
Accessible name announced by screen readers (`role="status"`, or `role="timer"` for the countdown variant). Override it to localize or to say WHAT is loading. When the spinner is purely decorative — another visible, associated loading text already exists nearby — pass `aria-hidden`.
legibility
"on-photo"
Reading-shadow preset for text over a photographic background (`on-photo`). Replaces the inline `style={{ textShadow }}` in the consumer (brasil_2030 radar, gap A3). Token emission is deferred until a 2nd consumer asks for the raw var (see backlog).
pageShell
boolean
Centers the element and caps the width at `theme.layout.pageMaxWidth`. The page shell of a classic centered layout.
readable
number | boolean
Makes the `color` legible against the page surface: `true` = WCAG AA (4.5), a number sets a custom floor. Ignored with `contrast`. See the Layout Props concept page for the full semantics.
size
string | number
Spinner size. Accepts a number (px) or a CSS string (`em`, `rem`, etc.). When omitted, uses `1em` to inherit the context's `font-size` — so the spinner stays naturally proportional wherever it is embedded.
transform
string
CSS `transform` value (e.g. `translate(-50%, -33px)`). Never leaks to the DOM as an attribute.
variant
"indeterminate" | "countdown"
indeterminate
`'indeterminate'` spins forever. `'countdown'` is a determinate ring that drains over `duration` and stays empty until `cycle` changes.

Besides the props above, every component accepts the layout props (spacing, color, flex/grid, sizing, border) — not repeated here.

See also

  • Storybook story: Components / Spinner
  • Determinate alternative: ProgressBar