# Button

> A press-responsive button with haptics that runs async actions on its own: it gathers into a spinner, then opens into a success or error state.

- Type: Component (actions)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/button
- Markdown: https://goodui.dev/native/components/button.md
- Registry item: https://goodui.dev/native/r/button.json
- Dependencies: react-native-reanimated
- Keywords: react native button, expo button, loading button, async button, success state button, haptic button, morphing button

## When to use

- The primary or secondary action of a screen: continue, save, send, subscribe.
- Actions backed by a request: return the promise from onPress and the button shows pending, success and error itself.
- Short labels that start with a verb.

## When not to use

- Irreversible actions such as deleting an account: use hold-to-confirm.
- Switching between views: use segmented-control.
- Toolbar icons without a label: use a plain Pressable with an accessibilityLabel.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/button.json
npx expo install --fix
```

The component lands in `components/goodui-native/button/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { Button } from "@/components/goodui-native/button/button";

export function SaveBar({ save }: { save: () => Promise<void> }) {
  return (
    <Button size="lg" successLabel="Saved" onPress={save}>
      Save changes
    </Button>
  );
}
```

## Examples

### Variants and sizes

```tsx
<Button>Continue</Button>
<Button variant="secondary">Not now</Button>
<Button variant="ghost">Learn more</Button>
<Button variant="destructive" size="sm">Delete</Button>
```

### A request that can fail

```tsx
<Button variant="secondary" errorLabel="Couldn't send" onPress={() => api.sendInvite(email)}>
  Send invite
</Button>
```

## API reference

### Button

`import { Button } from "@/components/goodui-native/button/button";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `string` | – | Button label. Sentence case, starts with a verb. |
| `onPress` | `() => unknown` | – | Return a promise and the button runs the whole result on its own: it gathers into a spinner while the promise is pending, then opens into a success or error state. Anything else (or nothing) is an instant action. |
| `variant` | `"primary" \| "secondary" \| "ghost" \| "destructive"` | `"primary"` | `primary` is the one main action; `secondary` and `ghost` are quieter; `destructive` is a soft red. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Heights 36, 48 and 56 pt. Small buttons keep a 44 pt touch target. |
| `leading` | `ReactNode` | – | Icon before the label, 18 to 20 pt, colored to match the label. |
| `disabled` | `boolean` | `false` | Dims the control and ignores presses. |
| `successLabel` | `string` | – | Short text shown with the check after the promise resolves. Omit to show the check alone. |
| `errorLabel` | `string` | – | Short text shown when the promise rejects. Same as `labels.error`. |
| `haptic` | `HapticName \| false` | `"tap"` | Haptic on press. `false` turns it off for this button. |
| `accessibilityLabel` | `string` | – | Overrides the label read by screen readers. |
| `accessibilityHint` | `string` | – | Describes what happens on activation, read after the label. |
| `labels` | `Partial<ButtonLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- accessibilityRole is button; disabled and busy are reported through accessibilityState.
- The accessibility label follows the state: 'Save changes, in progress', then the success or error label.
- Results are announced with AccessibilityInfo.announceForAccessibility.
- Heights below 44 pt get a hitSlop that brings the touch target to 44 pt.

## Motion

- Press: scales to 0.97 on the snappy spring and fills an overlay, both from touch down.
- Pending: the surface gathers into a circle on the morph spring; the layout never shifts.
- Result: color leads, the surface opens, then the check draws itself or the error appears; an error also shakes.
- Reduced motion: no scale, no shape change, no shake; states crossfade.

## Haptics

- tap on press (configurable with the haptic prop)
- success when the promise resolves
- error when it rejects

## Performance

- All animation runs on the UI thread through Reanimated shared values; React renders only on state changes.

## Notes for AI

- Return the promise from onPress to get loading, success and error for free. Do not add your own spinner or isLoading state.
- The label is children and must be a string. Put icons in the leading prop.
- It stretches in a column and hugs its label in a row. Use style={{ alignSelf: 'flex-start' }} to hug inside a column.
- Use one primary button per screen; secondary and ghost for the rest.

## Related

- [Hold to confirm](https://goodui.dev/native/components/hold-to-confirm.md): Confirms an irreversible action by holding: ink fills from the leading edge, the label inverts where the ink passes, and haptics tick at every quarter.
- [Segmented control](https://goodui.dev/native/components/segmented-control.md): Two to five views of the same thing with an elastic highlight that stretches toward the new segment, can be dragged, and ticks at every boundary. Includes a billing toggle.
- [Toast](https://goodui.dev/native/components/toast.md): Background confirmations that stack at the top, fan out on tap, swipe away up or sideways, and pause their timers while touched.

## Guidance for AI tools

Button: A press-responsive button with haptics that runs async actions on its own: it gathers into a spinner, then opens into a success or error state. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
