Spinner
The ring that says an operation is running.
On this page
Code
import { Spinner } from "@bosunkit/components/spinner";
import type { ReactElement } from "react";
export const description: string =
"A wait named for the crew: the ring, and what is loading, so a screen reader hears the same.";
export default function Saving(): ReactElement {
return <Spinner label="Saving the record" />;
}
Spinner is the ring that says work is in progress. It appears once an operation has run for more than a
second when the caller applies that delay: a fast operation can finish without showing one. The motion
rules allow exactly two loops — the alert flash, and this ring — so a spinner reads as progress, never as an alarm.
Named or silent. With a label, the spinner is a status region: the words under the ring are what a screen
reader announces. Without one it says nothing, and the caller names it — the text beside a button's pending spinner
does. Under reduced motion the ring does not turn; the wait is the words.
Sizes
Code
import { Spinner } from "@bosunkit/components/spinner";
import type { ReactElement } from "react";
export const description: string =
"Three sizes on the size classes: a spinner beside text takes the text's size, and the prop only matters on its own.";
export default function Sizes(): ReactElement {
return (
<div style={{ display: "flex", gap: "var(--space-md)", alignItems: "center" }}>
<Spinner size="sm" label="Loading" />
<Spinner size="md" label="Loading" />
<Spinner size="lg" label="Loading" />
</div>
);
}
The sizes are the size classes in em, so a spinner beside a button's pending label takes the label's size, and the
prop only matters where there is no text around to inherit from.
The caller owns the wait
Spinner renders immediately when mounted; the caller decides when an operation has waited long enough to show
it. It does not start work, measure progress, cancel a request or enforce a timeout. Name the operation with
label, or provide visible pending text in the containing control without a second announcement.
Use a skeleton when the final content's structure is known. Keep an error and recovery path for a failed or unusually long operation; an endless spinner is not a useful error state. Retain confirmed data while refreshing when it can be labeled accurately, and do not use a spinner to disguise an unavailable instrument value.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | undefined | – | What is loading, in a few words ("Finding the position"). Shown under the ring and announced through the spinner's status region, so the wait reaches a screen reader. Unset, the spinner is silent: the caller names it, as the text beside a button's pending spinner does. |
| size | SpinnerSize | undefined | "md" | How big the ring is: sm, md or lg; md unless set. |
| className | string | undefined | – | Classes added to the spinner's own. |
| testId | string | undefined | – | Becomes data-testid on the spinner's root. |