# Action sheet

> An iOS-style list of choices that springs up from the bottom edge in grouped cards; it follows the finger to be dragged away, and pulling it up stretches the gap between the cards before they spring back together.

- Type: Component (overlays)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/action-sheet
- Markdown: https://goodui.dev/native/components/action-sheet.md
- Registry item: https://goodui.dev/native/r/action-sheet.json
- Dependencies: react-native-gesture-handler, react-native-reanimated
- Keywords: react native action sheet, expo action sheet, ios action sheet, bottom menu, share options sheet, destructive action list

## When to use

- Two to eight choices about one thing the person just touched: a conversation, a photo, a card.
- Choices that include a destructive one, set apart in its own group.

## When not to use

- Forms, filters or long content: use bottom-sheet.
- Choices about an item in a list where the item should stay visible and in place: use context-menu.
- A single yes or no question: use alert-dialog.

## 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/action-sheet.json
npx expo install --fix
```

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

### Setup

- Wrap the app in GestureHandlerRootView and ThemeProvider (the sheet renders in its overlay layer).

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 { ActionSheet } from "@/components/goodui-native/action-sheet/action-sheet";

export function ConversationOptions({ open, onOpenChange, chat }: Props) {
  return (
    <ActionSheet
      open={open}
      onOpenChange={onOpenChange}
      title={chat.name}
      groups={[
        [
          { label: "Mute for 8 hours", onPress: () => mute(chat.id) },
          { label: "Pin to top", onPress: () => pin(chat.id) },
        ],
        [{ label: "Delete conversation", destructive: true, onPress: () => remove(chat.id) }],
      ]}
    />
  );
}
```

## Examples

### Icons, attached to the edge

```tsx
<ActionSheet
  open={open}
  onOpenChange={setOpen}
  variant="attached"
  title="Profile photo"
  actions={[
    { label: "Take photo", icon: (p) => <CameraIcon {...p} />, onPress: openCamera },
    { label: "Choose from library", icon: (p) => <ImageIcon {...p} />, onPress: openLibrary },
    { label: "Remove photo", icon: (p) => <TrashIcon {...p} />, destructive: true, onPress: removePhoto },
  ]}
/>
```

### A choice that must be made

```tsx
<ActionSheet
  open={open}
  onOpenChange={setOpen}
  dismissible={false}
  title="You have unsaved changes"
  cancelLabel="Keep editing"
  actions={[
    { label: "Save draft", onPress: saveDraft },
    { label: "Discard changes", destructive: true, onPress: discard },
  ]}
/>
```

## API reference

### ActionSheet

`import { ActionSheet } from "@/components/goodui-native/action-sheet/action-sheet";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` (required) | `boolean` | – | Whether the sheet is shown. Closing animates out before it unmounts. |
| `onOpenChange` (required) | `(open: boolean) => void` | – | Called with false when an action is chosen, on cancel, on a drag down, a backdrop tap or back. |
| `title` | `string` | – | Short context above the actions, such as the item's name. |
| `message` | `string` | – | One line explaining the choice. |
| `actions` | `ActionSheetAction[]` | – | One group of actions. Use `groups` for several. |
| `groups` | `ActionSheetAction[][]` | – | Several groups, each shown as its own card (inset) or separated by a band (attached). |
| `cancelLabel` | `string \| null` | – | Label of the cancel row. Pass `null` to leave it out. Same as `labels.cancel`. |
| `variant` | `"inset" \| "attached"` | – | `inset` floats above the bottom edge as cards, like iOS; `attached` is one sheet with a grabber on the edge. |
| `dismissible` | `boolean` | – | Allow closing by dragging down, tapping outside, back or Escape. Default `true`. |
| `haptic` | `boolean` | – | Haptic when an action is chosen. Default `true`. |
| `labels` | `Partial<ActionSheetLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The sheet is modal for screen readers (accessibilityViewIsModal) and announces its title when it opens.
- Each row is a button with a label and a disabled state; double tap activates it.
- Every row is at least 56 pt tall; on the web rows are focusable and Enter or Space chooses, Escape closes.
- Android back closes the sheet when it is dismissible.

## Motion

- Enter: the cards rise from below the edge on the smooth spring while the backdrop fades in.
- Drag: the sheet follows the finger 1:1 downward and the backdrop lightens with it; release projects the velocity and either dismisses or springs back.
- Pull up: the sheet resists more the further it goes, and the action cards travel further than the cancel card so the gap between them stretches; on release they spring back together on the bouncy spring.
- Rows fill on touch down; a drag that moves the sheet cancels the press.
- Reduced motion: no slide and no stretch; the sheet fades in and out.

## Haptics

- tap when an action is chosen (not for cancel)

## Performance

- Drag, backdrop and the stretch run on the UI thread; a closed sheet mounts nothing.

## Notes for AI

- onPress runs after the sheet has finished closing, so it is safe to open an alert, another sheet or navigate from it.
- Use groups to separate the destructive action from the others; cancel is added automatically (cancelLabel={null} removes it).
- Rows align to the leading edge when any action has an icon; otherwise they are centered like iOS.
- For an irreversible destructive choice, follow up with an Undo toast rather than a second confirmation.

## Related

- [Bottom sheet](https://goodui.dev/native/components/bottom-sheet.md): A sheet that rests at detents, follows the finger 1:1, settles with velocity and hands drags off to its scroll view.
- [Context menu](https://goodui.dev/native/components/context-menu.md): Long-press an element and it lifts out of the page above a dimmed backdrop while a menu springs out beside it; keep the finger down, slide onto an item and release to choose. Right-click opens it on the web.
- [Alert dialog](https://goodui.dev/native/components/alert-dialog.md): A centered alert that springs in from slightly larger; touch an action and slide across the others with a tick per action, tap outside and it nudges, and async actions keep it open with a spinner until they settle.
- [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

Action sheet: An iOS-style list of choices that springs up from the bottom edge in grouped cards; it follows the finger to be dragged away, and pulling it up stretches the gap between the cards before they spring back together. 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
