# Loading Button

> Carries its own outcome: spinner, tick or shake.

While the work runs it spins and refuses repeat clicks, then ticks for success or shakes for failure and returns. The width eases between states.

- Family: Buttons
- Plan: Creative. Included with Creative.
- Requires: React, Tailwind CSS v4 and the motion package
- Page with a live preview: https://galactrex.com/ui/loading-button/
- Licence: https://galactrex.com/ui/license/

## Install

```sh
npx @galactrex/ui login   # once, with an API key from https://galactrex.com/account/
npx @galactrex/ui add loading-button
```

It installs to `components/galactrex/loading-button.tsx`, with the shared motion helpers beside it. First time in a project: `npx @galactrex/ui init` (setup guide: https://galactrex.com/docs/ui-components-setup/).

## Usage

```tsx
import { LoadingButton } from "@/components/galactrex/loading-button";

<LoadingButton onClick={() => api.save(draft)}>Save draft</LoadingButton>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | — | Label. |
| `onClick` | `(e) => Promise<unknown> \| unknown` | — | The work; pending shows progress. |
| `tone` | `"solid" \| "outline" \| "ghost"` | `"solid"` | Look. |
| `successLabel / errorLabel` | `ReactNode` | `"Done" / "Failed"` | Result labels. |
| `resultFor` | `number` | `1600` | Ms a result stays. |
| `...button props` | `ButtonHTMLAttributes` | — | onClick, disabled, type and the rest. |

## Variants

- **Save.** Await the request in onClick.
- **Fails sometimes.** Throw to show the error shake.

## Accessibility

- aria-busy while working; results announced politely.
- Repeat clicks are ignored while busy.

## Motion

- Layout animation for width, cross-fade for states, a short shake on error.
- Reduced motion: no shake or travel.
