Menu
Menu drops a list of actions from the button that opens it: the β― on a card, a "Sort" button, a toolbar icon. Long-press instead, and it's a context menu. It takes the same actions as ActionSheet, and presentation="sheet" opens them as one.
It's pure JavaScript, so unlike native menus it runs in Expo Go and on the web, and looks the same as the rest of your pickers: iOS, Material or web.
import { Menu } from '@elik56a/react-native-pickers';
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}> <Text>Last action: {last}</Text> <Menu accessibilityLabel="Note options" actions={[ { label: 'Edit', icon: icon('βοΈ'), onPress: () => setLast('Edit') }, { label: 'Duplicate', icon: icon('π'), onPress: () => setLast('Duplicate') }, { label: 'Pin to top', icon: icon('π'), selected: pinned, onPress: () => setPinned((p) => !p) }, { label: 'Delete', icon: icon('ποΈ'), destructive: true, onPress: () => setLast('Delete') }, ]} > <Text style={{ fontSize: 22, paddingHorizontal: 12 }}>β―</Text> </Menu> </View>
Try the propsβ
aligntitlepresentation<Menu actions={actions} accessibilityLabel="Note options">
β¦
</Menu>
Actionsβ
Each action is the same as in an ActionSheet: { label, onPress, description, icon, selected, destructive, disabled, testID }. A tap closes the menu and onPress runs once it has closed, so an action can open a sheet or a modal.
The iOS design puts icons after the label with a hairline between actions, as iOS menus do; Material and the web put them first.
The triggerβ
children is the trigger. A node is wrapped in a pressable that opens the menu; name it for screen readers with accessibilityLabel. To use your own button, pass a function: it gets open and visible.
This one is a "Sort" button with a title, a β on the current order and a line under each choice:
<View style={{ alignItems: 'center' }}> <Menu title="Sort results by" actions={SORTS.map((s) => ({ ...s, selected: s.label === sort, onPress: () => setSort(s.label) }))} > {({ open, visible }) => ( <Pressable onPress={open} accessibilityRole="button" style={{ flexDirection: 'row', gap: 6, paddingVertical: 8, paddingHorizontal: 14, borderRadius: 999, borderWidth: 1, borderColor: '#E8E4DC' }} > <Text>Sort: {sort}</Text> <Text>{visible ? 'β΄' : 'βΎ'}</Text> </Pressable> )} </Menu> </View>
Context menuβ
openOnLongPress opens the menu on a long press instead of a tap, as a message bubble or a photo does.
<View style={{ alignItems: 'center' }}> <Menu openOnLongPress accessibilityLabel="Message, long-press for options" actions={[ { label: 'Reply' }, { label: 'Copy' }, { label: 'Forward', description: 'To a chat or a group' }, { label: 'Delete', destructive: true }, ]} > <View style={{ backgroundColor: '#F4F2EE', padding: 12, borderRadius: 16 }}> <Text>Long-press me π</Text> </View> </Menu> </View>
Where it opensβ
The menu opens under the trigger, or above it when there's more room there. align picks the edge it lines up with:
'auto'(default): the trigger's left edge, or its right edge when the trigger sits in the right half of the window, so a β― at the end of a row opens inwards.'start'/'end': the trigger's start or end edge, flipped in right-to-left layouts.
width sets the menu's width: 250 in the iOS design and 200 otherwise.
presentation="sheet" opens the same actions as an ActionSheet instead, from the bottom of the screen.
Propsβ
| Prop | Type | Default | Description |
|---|---|---|---|
actions | MenuAction[] | required | The actions, top to bottom. Same shape as ActionSheetAction. |
children | ReactNode | ({ open, visible }) => ReactNode | required | The trigger: a node that opens the menu, or a function for your own button. |
title | string | β | A small heading above the actions. |
align | 'auto' | 'start' | 'end' | 'auto' | The trigger edge the menu lines up with. |
width | number | 250 iOS / 200 other | The menu's width. |
presentation | 'menu' | 'sheet' | 'menu' | A menu by the trigger, or an action sheet. |
openOnLongPress | boolean | false | Open on a long press, as a context menu. |
disabled | boolean | false | The trigger doesn't open the menu. |
onOpen / onClose | () => void | β | When it opens; when it closes for any reason, before an action runs. |
accessibilityLabel | string | β | What screen readers call the trigger, such as 'More options'. |
theme / design | See Theming and Designs. | ||
style | ViewStyle | β | The box around the trigger. |
styles | { trigger, menu, header, title, action, actionLabel, actionDescription, check } | β | Slot styles. |
testID | string | β | On the trigger; the menu gets ${testID}-menu. |
Accessibilityβ
The trigger is a button that reports whether the menu is expanded. Each action is a button named by its label and description, marked disabled or selected. A tap outside, Android's Back and Escape on the web close the menu, and on the web arrow keys move between actions.
Relatedβ
- ActionSheet: the same actions from the bottom of the screen
- SelectPicker: pick a value, not an action
- Migrate from @react-native-menu/menu, zeego or react-native-popup-menu