A small loading indicator for work of unknown length: inline with text, inside a button, or over a whole area.
import { Spinner } from "@rdloom/react";
export default function SpinnerSizesExample() {
return (
<div className="flex w-full items-center justify-center gap-6">
<Spinner size="sm" label="Loading, small" />
<Spinner size="md" label="Loading, medium" />
<Spinner size="lg" label="Loading, large" />
</div>
);
}Installation
npx rdloom add spinnerCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/spinner.jsonWorks, but without upgrade tracking.
Usage
import { Spinner } from "@rdloom/react";
<div className="flex w-full items-center justify-center gap-6">
<Spinner size="sm" label="Loading, small" />
<Spinner size="md" label="Loading, medium" />
<Spinner size="lg" label="Loading, large" />
</div>Inline
Checking your connection
import { Spinner } from "@rdloom/react";
export default function SpinnerInlineExample() {
return (
<div className="flex w-full justify-center">
<p className="flex items-center gap-2 text-sm text-[var(--rd-color-text-muted)]">
<Spinner size="sm" decorative />
<span>Checking your connection</span>
</p>
</div>
);
}Button
import { Button, Spinner } from "@rdloom/react";
export default function SpinnerButtonExample() {
return (
<div className="flex w-full justify-center">
<Button variant="secondary" isDisabled>
<Spinner size="sm" decorative className="text-current" />
Saving changes
</Button>
</div>
);
}Overlay
Invoices
INV-1042, INV-1043, INV-1044
INV-1045, INV-1046, INV-1047
import { Spinner } from "@rdloom/react";
export default function SpinnerOverlayExample() {
return (
<div className="flex w-full justify-center">
<div className="relative w-full max-w-sm rounded-[var(--rd-radius-control)] border border-[var(--rd-color-border-default)] p-4 text-sm">
<p className="font-medium">Invoices</p>
<p className="mt-1 text-[var(--rd-color-text-muted)]">INV-1042, INV-1043, INV-1044</p>
<p className="mt-1 text-[var(--rd-color-text-muted)]">INV-1045, INV-1046, INV-1047</p>
<div className="absolute inset-0 flex items-center justify-center rounded-[inherit] bg-[var(--rd-color-surface-default)]/80">
<Spinner size="lg" label="Loading invoices" />
</div>
</div>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
sizeDiameter: 16, 20 or 32 px. | "sm" | "md" | "lg" | "md" |
labelThe accessible name, read by screen readers as visually hidden text. Say what is loading when it is not obvious, e.g. "Loading invoices". | string | "Loading" |
decorativeHides the spinner from assistive technology. Use it when the surrounding control already says it is busy, such as a button labelled "Saving". | boolean | false |
Accessibility
Role status, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
Not interactive.
Screen readers announce
- The label is announced when the spinner appears
- A decorative spinner is not announced
What your code must do
- Not focusable and not interactive
- Has role status with the label as visually hidden text, so the busy state is announced politely
- With decorative set it is aria-hidden and has no role
- With reduced motion the rotation stops and the static arc remains, so it still reads as a loading mark
- Never the only sign of progress for work that takes long: pair it with text
Guidelines
Use it when
- A short wait of unknown length
- A button that is saving or sending
- Covering an area while its content loads
Avoid it when
- The wait has a known length: use Progress
- A page or list shape is known: use Skeleton
Don't
- Several spinners on one screen at once
- A spinner with no text for waits over a few seconds
- Leaving the default label when the page has many spinners
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-action-primary--rd-color-text-muted