# Spinner

An accessible React spinner for Next.js and Tailwind CSS: one line going round an infinity that fills it once the task is done.

Source: https://ui.fewtone.com/components/spinner

## Installation

```bash
npx shadcn@latest add https://ui.fewtone.com/r/spinner.json
```

## Usage

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

export function Loading() {
  return <Spinner />
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 14, 16 or 20px, the icon sizes, so it can stand in for an icon. |
| `aria-label` | `string` | `"Loading"` | What's loading, for screen readers. Inside a button or beside text that already says it, pass aria-hidden instead. |
| `done` | `boolean` | `false` | The task is finished: the line fills the whole infinity and stops. |
| `doneLabel` | `string` | `"Done"` | Its name for screen readers once done. |

**When to use it:** Use Spinner while something loads and there's no way to tell how far along it is: a panel fetching data, a button submitting. Set done when it finishes to fill the infinity. Use LoadingButton for a button that shows loading, success and error by itself.
**npm packages (installed by the CLI):** class-variance-authority@^0.7.1, motion@^14.0.0.

## Examples

### Sizes

The three icon sizes, and one inside a button. One line goes round the infinity at an even pace; with reduced motion it pulses instead.

```tsx
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";

/* The three sizes and one in a button, named underneath */
export default function SpinnerSizes() {
  return (
    <ul className="flex flex-wrap items-end justify-center gap-x-10 gap-y-6">
      <li className="flex flex-col items-center gap-2">
        <Spinner size="sm" />
        <span className="text-xs text-muted-foreground">Small</span>
      </li>
      <li className="flex flex-col items-center gap-2">
        <Spinner />
        <span className="text-xs text-muted-foreground">Medium</span>
      </li>
      <li className="flex flex-col items-center gap-2">
        <Spinner size="lg" />
        <span className="text-xs text-muted-foreground">Large</span>
      </li>
      <li className="flex flex-col items-center gap-2">
        {/* The label already says what's happening, so the spinner is hidden from screen readers */}
        <Button variant="outline" disabled>
          <Spinner aria-hidden />
          Saving…
        </Button>
        <span className="text-xs text-muted-foreground">In a button</span>
      </li>
    </ul>
  );
}
```

### Finish

Set done when the task is finished: the line stops where it is and grows until it fills the whole infinity. Back to loading, it shrinks and travels on.

```tsx
"use client";

import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { useState } from "react";

/* The finish: done fills the whole infinity. The row is the status, so it's read as its text. */
export default function SpinnerFinish() {
  const [done, setDone] = useState(false);
  return (
    <div className="flex flex-col items-center gap-4">
      <div role="status" className="flex items-center gap-2 text-sm text-muted-foreground">
        <Spinner aria-hidden done={done} />
        {done ? "Saved" : "Saving…"}
      </div>
      <Button variant="outline" size="sm" onClick={() => setDone((current) => !current)}>
        {done ? "Restart" : "Finish"}
      </Button>
    </div>
  );
}
```

### With text

Beside text, put role="status" on the row and hide the spinner, so it's read once as the text.

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

/* Beside text, the row is the status, so it's read once as "Loading comments…" */
export default function SpinnerWithText() {
  return (
    <div role="status" className="flex items-center gap-2 text-sm text-muted-foreground">
      <Spinner aria-hidden />
      Loading comments…
    </div>
  );
}
```

## Related

- [Loading Button](https://ui.fewtone.com/components/loading-button.md): An accessible React loading button for Next.js that shows loading, success and error states for async actions, without changing width.
- [Button](https://ui.fewtone.com/components/button.md): An accessible React button for Next.js and Tailwind CSS with six variants, three sizes, icons and a soft tint on hover and press, built on a native button.
- [Badge](https://ui.fewtone.com/components/badge.md): An accessible React badge for Next.js and Tailwind CSS: small status labels in five variants, including one with a light travelling round its edge.
