# Loading button (goodtabs UI)

A button with a pending state that shows a spinner and keeps its width.

## Install

```bash
npx shadcn@latest add https://ui.goodtabs.co/r/loading-button.json
```

Or, with the @goodtabs namespace configured in components.json: `npx shadcn@latest add @goodtabs/loading-button`

## Usage

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

<LoadingButton type="submit" loading={pending} loadingText="Saving">
  Save
</LoadingButton>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| loading | `boolean` | `false` | Shows the spinner, sets aria-busy and disables the button. |
| loadingText | `string` |  | Replaces the children while loading. Leave out to show only the spinner. |
| variant, size, type | `Button props` | `"default", "default", "button"` | Every shadcn Button prop passes through. |

## When to use

- Form submits and any action that takes more than a moment to settle
- When the button sits in a row or footer where a width change would shift its neighbours
- With useActionState or useTransition, passing pending straight into loading

## When not to use

- For actions that settle instantly; a spinner that flashes for 50ms reads as a glitch
- To block a whole form: disable the fields too, or use a dialog with a pending state
- For page level progress; use a progress bar or skeleton

## Notes for agents

- Owns no state: pass loading from a transition, action state or a fetch, and the button handles aria-busy and disabled
- The label stays in the layout with visibility hidden, so the width is at least the label's; a longer loadingText grows it
- The spinner stops under prefers-reduced-motion and still reads as a loader icon
- type defaults to button; pass type="submit" in forms

Docs: https://ui.goodtabs.co/components/loading-button