Sizes
Extra small
16px diameter. Dense inline UI, inside a small Button or IconButton.
<Loader size="xs" />Small
24px diameter.
<Loader size="sm" />Medium
32px diameter. The default size.
<Loader size="md" />Large
48px diameter.
<Loader size="lg" />Extra large
64px diameter. Full-page or full-panel loading states.
<Loader size="xl" />Inverse
Inside an inverse <Surface> this happens automatically — Surface is the source of truth. Pass inverse explicitly only for a dark or branded background that isn’t a Surface.
Inside an inverse Surface
Surface is the source of truth: inside an inverse Surface the loader adopts inverse ink automatically — no inverse prop needed. (The rendered preview below passes inverse explicitly as a docs-site-only workaround — this ComponentPreview's client:load island doesn't carry Surface's tone context to nested framework components, so the automatic adoption shown in the code sample can't run here. Storybook and real consumer apps pick it up from Surface automatically.)
<Surface tone="inverse" className="rounded-lg p-6">
<Loader size="lg" />
</Surface>When to use
Use Loader for an indeterminate wait — you don’t know how long an operation will take or how far through it is. It’s the default busy indicator inline in content, inside a busy control, or as a full-panel placeholder while data is fetched.
Button and IconButton render a Loader internally for their loading state; you don’t need to compose it manually there.
When not to use
- Determinate/measurable progress: if you know the percentage complete, use a progress bar, not
Loader— it has no percentage mode. - Content placeholders: while a piece of layout is about to appear (a card, a list row, an avatar), use a skeleton, not a spinner.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Diameter preset: 16 / 24 / 32 / 48 / 64px. |
| inverse | boolean | - | Force the inverse (light) ink. Defaults to adopting the surrounding Surface tone. |
| label | string | 'Loading' | Accessible name, announced via role="status". |
| id | string | - | Web — passed to the root span. |
| data-testid | string | - | Web — passed to the root span for test targeting. |
| nativeID | string | - | Native — passed to the root View. |
| testID | string | - | Native — passed to the root View for test targeting. |
| accessibilityHint | string | - | Native — passed to the root View. |
Import
Web
import { Loader } from '@checkatrade/components-web';Native
import { Loader } from '@checkatrade/components-native';Basic usage
<Loader />
<Loader size="lg" />
<Loader inverse /> // or automatic inside <Surface tone="inverse">
<Loader label="Loading results" /> Platform status
| Platform / Area | Status |
|---|---|
| Design (Figma) | Beta |
| Web (React) | Beta |
| Native (React Native) | Beta |
| iOS (Swift) | Planned |
| Android (Kotlin) | Planned |
| Accessibility audit | Planned |
Accessibility
- The root element carries
role="status"on web (accessibilityRole="progressbar"on native) with an accessible name fromlabel, so assistive tech announces the busy state without reading the decorative spinner graphic, which is hidden from the accessibility tree. - Colour is tone-aware: it resolves from the surrounding
Surfacetone (or the explicitinverseprop) rather than a fixed value. - The spinner keeps animating under
prefers-reduced-motion— it’s essential busy feedback, not decorative motion.
No releases yet.