---
id: "packages/active-ui/spinner"
title: "Spinner"
description: "A busy indicator that is decoration beside text, or a named status when it stands alone."
url: "/packages/active-ui/spinner"
locale: "en"
revision: "5083cffe6c7792c1"
---

# Spinner

A spinner shows that something is busy for a short, unknown time. It is an icon that turns. Beside
text that already says what happens ("Saving…"), it is decoration. Standing alone, it needs a name,
and then it is announced as a status. For a task with a measure, use a
[progress bar](/packages/active-ui/progress); for a whole page that loads, the composed
[`LoadingScreen`](/packages/active-ui/loading-screen).

**Example: Named, decorative, and in a button**

Client: Runs entirely in your browser. Source: `examples/ui/spinner.tsx`.

```tsx
// apps/docs/examples/ui/spinner.tsx
'use client';

import {Button} from '@workspace/active-ui/components/primitives/button';
import {Spinner} from '@workspace/active-ui/components/primitives/spinner';

/**
 * An indicator that something is busy. On its own it needs a name
 * (`label`) and is announced as a status; beside text that already says
 * what happens, it is decoration and stays silent.
 */
export default function SpinnerExample() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-6">
      <Spinner label="Loading the participants" className="size-6" />
      <p className="text-muted-foreground flex items-center gap-2 text-sm">
        <Spinner />
        Checking the addresses…
      </p>
      <Button disabled>
        <Spinner data-icon="inline-start" />
        Saving…
      </Button>
    </div>
  );
}
```

## Usage

```tsx
import {Spinner} from '@workspace/active-ui/components/primitives/spinner';

<Spinner label="Loading the participants" />;

<Button disabled>
  <Spinner data-icon="inline-start" />
  Saving…
</Button>;
```

## Decorative or named

- **Without `label`**, the spinner is `aria-hidden`: the text beside it, the button around it or a
  heading already names the state. Two announcements of the same state are noise.
- **With `label`**, it is a `role="status"` named by the label, announced when it appears. Use this
  when the spinner is the only thing shown.

There is no default label: the package does not translate, and an English "Loading" would replace
the name of the button around it.

## What your application provides

- `label` when the spinner stands alone, in your reader's language.
- The size, with a `size-*` class (default `size-4`), and the colour, which follows the text.

## API

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | `string` | no | Accessible name of the loading state. **Optional, and deliberately without a default**.  Without `label` the spinner is decorative: `aria-hidden`, no role, no name. That is the right behaviour whenever something around it already names the state — a button with its own name, a region with a heading, a text next to it. Two announcements for the same state are noise.  With `label` the spinner itself becomes the announcement: `role="status"` plus the given name. Use that when the spinner is the only thing shown.  The name comes translated from the caller; `active-ui` does not translate. There is no English default: a fixed name would address every other language in English, and where the spinner is a button's only content while saving (`SubmitButton`), it would replace the button's own translated name. |

**SpinnerProps**

```ts
interface SpinnerProps extends React.ComponentProps<'svg'> {
  label?: string;
}
```

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Spinner documentation of shadcn/ui (MIT licence) to this
> package; see [Third-party notices](/third-party-notices).
