In React Native, how do you show a native iOS action sheet with ActionSheetIOS.showActionSheetWithOptions, and what happens if the same call runs on Android?
answer
- UIAlertController in action-sheet style
- options array of button titles
- cancel and destructive indices
- callback gets the tapped index
- Android: invariant error, no sheet
basics
~20 sActionSheetIOS.showActionSheetWithOptions takes an options array of button titles plus indices for cancel and destructive buttons, and calls back with the zero-based index tapped. It is iOS-only: on Android the native module is missing and the call throws.
solid answer
~40 s`ActionSheetIOS.showActionSheetWithOptions(options, callback)` presents iOS's native action sheet. `options.options` is the required array of button titles; `cancelButtonIndex` marks the cancel button, `destructiveButtonIndex` (a number or an array) styles destructive ones in red, `disabledButtonIndices` disables some, and `title`, `message`, `tintColor` and `userInterfaceStyle` adjust the look. The callback receives the zero-based index of the tapped button, so the app maps indices back to actions — keep the titles and the handlers in one array to avoid off-by-one bugs. On iPad the sheet is a popover; `anchor` takes a native node handle to point it at a view. On Android the underlying `ActionSheetManager` module does not exist, so the call fails an invariant and throws; Android needs a different UI, such as a bottom sheet or `Alert.alert`, whose Android dialog shows at most three buttons.
code
tsx · 21 linesimport { ActionSheetIOS } from 'react-native';
type Entry = { title: string; run?: () => void; destructive?: boolean; cancel?: boolean };
export function showPostActions(onShare: () => void, onCopyLink: () => void, onReport: () => void) {
const entries: Entry[] = [
{ title: 'Share post', run: onShare },
{ title: 'Copy link', run: onCopyLink },
{ title: 'Report post', run: onReport, destructive: true },
{ title: 'Cancel', cancel: true },
];
ActionSheetIOS.showActionSheetWithOptions(
{
options: entries.map(e => e.title),
destructiveButtonIndex: entries.findIndex(e => e.destructive),
cancelButtonIndex: entries.findIndex(e => e.cancel),
},
buttonIndex => entries[buttonIndex]?.run?.(),
);
}go deeper
Recall the call shape: an options array of titles, cancel and destructive indices, and a callback that receives the tapped index.
Explain index-based dispatch and how to keep it safe, the iPad popover and anchor, and the Android failure mode.
Design one cross-platform action menu API that uses the native iOS sheet and an Android component without duplicating action logic.
Decide when native platform pickers are worth their divergence versus one shared menu component across the product.
## What an action sheet is An **action sheet** is the iOS pattern for "choose one of these actions about the thing you just touched": a stack of buttons that slides up from the bottom, with **Cancel** separated at the end and dangerous actions in red. React Native exposes it through **`ActionSheetIOS`**, a thin JavaScript wrapper over a native iOS module that builds a `UIAlertController` in action-sheet style. In a community forum app, long-pressing a post is the classic trigger: *Share post*, *Copy link*, *Report post*, *Cancel*. ## The call ```tsx ActionSheetIOS.showActionSheetWithOptions( { options: ['Share post', 'Copy link', 'Report post', 'Cancel'], destructiveButtonIndex: 2, cancelButtonIndex: 3, title: 'Post by @river', }, buttonIndex => { // 0-based index into options }, ); ``` | Option | Type | Effect | | --- | --- | --- | | `options` | string array, **required** | Button titles, in order | | `cancelButtonIndex` | number | Styles that button as Cancel | | `destructiveButtonIndex` | number or number array | Styles one or several buttons as destructive | | `disabledButtonIndices` | number array | Greys out and disables those buttons | | `title`, `message` | string | Text above the buttons | | `tintColor`, `cancelButtonTintColor`, `disabledButtonTintColor` | color | Button title colours | | `userInterfaceStyle` | `'light'` or `'dark'` | Forces an appearance instead of following the system | | `anchor` | number | Native node handle the iPad popover points at | The **callback** receives one number: the zero-based index of the tapped button. There are no per-button `onPress` handlers, which is the main source of bugs — reorder the titles and every index-based `switch` silently maps to the wrong action. ## Keeping indices safe 1. Build one array of `{ title, run }` entries. 2. Derive `options` from the titles and compute `cancelButtonIndex` and `destructiveButtonIndex` with `findIndex`, never as literals. 3. In the callback, call `entries[buttonIndex].run()`. The native module also guards the callback so it runs **at most once** per sheet; its source notes that tapping outside the sheet on iPad can trigger a button handler more than once, and only the first reaches JavaScript. ## iPad On iPad an action sheet is shown as a **popover**. React Native always gives it a source view, so it never needs an anchor to appear: without `anchor` the popover is presented from the root view with no arrow; with `anchor` set to a node handle (for example `findNodeHandle(ref.current)`) it points at that view. ## Dismissing it from code `ActionSheetIOS.dismissActionSheet()` closes the topmost action sheet that React Native presented, and logs a warning if none is showing. In a forum app that is useful when the post disappears while the sheet is open — a moderator removed it, or a live update replaced the thread — so the user cannot pick *Report* on a post that no longer exists. The callback of a sheet dismissed this way should not be relied on for cleanup; keep the state change in the code that calls `dismissActionSheet()`. ## Action sheet or alert on iOS? - **Action sheet**: several actions about the thing the user just touched, slid up from the bottom. - **`Alert.alert`**: a centred dialog for a message or a short confirmation, such as *Report this post?* with *Cancel* and *Report*. A typical report flow uses both: the sheet to choose *Report post*, then an alert or prompt to confirm or collect a reason. ## The share sheet is a sibling method `ActionSheetIOS.showShareActionSheetWithOptions(options, failureCallback, successCallback)` opens the iOS **share sheet** for a `url` and/or `message`, with `subject` and `excludedActivityTypes`. For cross-platform sharing React Native also has the core `Share` API, which is usually what a forum app's *Share post* button calls. ## What happens on Android `ActionSheetIOS` is exported on every platform, but its native module is registered only on iOS. On Android the module lookup returns nothing, and `showActionSheetWithOptions` fails its first `invariant` checks with **"ActionSheetManager doesn't exist"** — an exception, not a silent no-op. Options for Android: - **`Alert.alert`** with buttons — but the Android dialog uses **at most three** buttons (neutral, negative, positive) and ignores the rest, so a four-action menu does not fit. - **A bottom sheet or menu component** rendered by the app, usually shared by both platforms when design wants identical behaviour. - **A platform branch** that uses the native sheet on iOS and the component on Android. ## Interview summary Name the required `options` array, the cancel and destructive indices, the index-based callback and the Android failure mode. Mentioning index drift, the iPad popover and the three-button limit of Android alerts shows you have shipped one.
- In React Native's ActionSheetIOS, how do you mark two buttons as destructive?Pass an array to `destructiveButtonIndex`, for example `destructiveButtonIndex: [2, 3]`. The JavaScript wrapper accepts a number or an array and normalises both into a list of destructive indices before calling the native module.
- Why can't a four-action React Native menu reuse Alert.alert on Android?React Native's Android alert maps buttons onto the dialog's neutral, negative and positive slots and keeps at most three, ignoring the rest. A menu with four actions plus cancel needs a bottom sheet or menu component instead.
saying these in an interview costs you the question
- ActionSheetIOS silently does nothing on Android
- Each option has its own onPress handler
- destructiveButtonIndex only accepts a single number
- An iPad action sheet crashes without an anchor
- Alert.alert can show any number of buttons on Android