# Spinner

Indicate that an operation is still in progress.

Loading spinner

[Complete TSX file](/examples/spinner/default.tsx)

```tsx
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return <Spinner />;
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/spinner
```

With npm: `npx shadcn@4.18.0 add @siteplane/spinner`. [Registry JSON](/registry/spinner.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/input-group
```

## Usage

```tsx
import { Spinner } from "@/siteplane/components/spinner";

<Spinner />;
```

## Variants

Select an indeterminate visual treatment; none of these variants reports numeric progress.

Spinner variants

[Complete TSX file](/examples/spinner/variants.tsx)

```tsx
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <div className="flex flex-wrap items-center gap-6">
      {(
        ["default", "ring", "dots", "bars", "spokes", "pulse", "comet"] as const
      ).map((variant) => (
        <div key={variant} className="grid justify-items-center gap-2">
          <Spinner variant={variant} />
          <span className="text-sm">{variant}</span>
        </div>
      ))}
    </div>
  );
}
```

## Sizes

Without a size class, the default, comet, ring, pulse and spokes variants are 16 px. Scale a spinner with size utilities: those variants follow the root size, dots keep their mark size and bars scale only in height.

Spinner sizes

[Complete TSX file](/examples/spinner/sizes.tsx)

```tsx
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <div className="flex items-center gap-6">
      <Spinner className="size-3" />
      <Spinner className="size-4" />
      <Spinner className="size-6" />
      <Spinner className="size-8" />
    </div>
  );
}
```

## Labels

Name what is loading. When visible text already describes the operation, hide the spinner from assistive technology. For a pending button, use the [Button](/docs/components/button/) `loading` prop or `LoadingButton` instead of placing a spinner in the button.

Spinner with loading label

[Complete TSX file](/examples/spinner/label.tsx)

```tsx
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <div className="flex flex-wrap items-center gap-6">
      <div
        className="flex items-center gap-2 text-muted-foreground text-sm"
        role="status"
      >
        <Spinner aria-hidden="true" />
        Loading projects…
      </div>
      <Spinner aria-label="Loading comments" />
    </div>
  );
}
```

## Input group

Show a spinner in an input group addon while a search or validation runs.

Search field while loading

[Complete TSX file](/examples/spinner/input-group.tsx)

```tsx
"use client";

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/siteplane/components/input-group";
import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <InputGroup className="max-w-sm">
      <InputGroupInput
        aria-label="Search projects"
        defaultValue="Onboarding"
        type="search"
      />
      <InputGroupAddon align="inline-end">
        <Spinner aria-label="Searching" />
      </InputGroupAddon>
    </InputGroup>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| variant | SpinnerVariant | default | default, ring, dots, bars, spokes, pulse or comet. |
| className | string | unset | Additional classes; size utilities customize SVG, ring, pulse and spokes dimensions. |
| aria-label | string | Loading | Accessible loading label. |
| role | ARIA role | status | Status semantics unless overridden. |

`SpinnerProps` and `SpinnerVariant` are exported. The props and the ref follow the element that the variant renders, an SVG for `default` and `comet` and a div for the others. Dots size their marks directly, and bars keep a fixed bar width while their height follows the root.

[Read the installed source](/source/spinner).

## Usage notes

Use [Progress](/docs/components/progress/) for measurable completion. A spinner does not start, cancel or finish a request. Your application owns pending/error state and when to remove it.
