Skip to main content
Version: Next 🚧

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';
Loading…
<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​

Platform
align
title
presentation
<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:

Loading…
<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.

Loading…
<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​

PropTypeDefaultDescription
actionsMenuAction[]requiredThe actions, top to bottom. Same shape as ActionSheetAction.
childrenReactNode | ({ open, visible }) => ReactNoderequiredThe trigger: a node that opens the menu, or a function for your own button.
titlestringβ€”A small heading above the actions.
align'auto' | 'start' | 'end''auto'The trigger edge the menu lines up with.
widthnumber250 iOS / 200 otherThe menu's width.
presentation'menu' | 'sheet''menu'A menu by the trigger, or an action sheet.
openOnLongPressbooleanfalseOpen on a long press, as a context menu.
disabledbooleanfalseThe trigger doesn't open the menu.
onOpen / onClose() => voidβ€”When it opens; when it closes for any reason, before an action runs.
accessibilityLabelstringβ€”What screen readers call the trigger, such as 'More options'.
theme / designSee Theming and Designs.
styleViewStyleβ€”The box around the trigger.
styles{ trigger, menu, header, title, action, actionLabel, actionDescription, check }β€”Slot styles.
testIDstringβ€”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.