Reference version

Menu

A menu compatible with @react-native-menu/menu.

Android
iOS
Included in Expo Go
Bundled version:
~56.0.6

For the complete documentation index, see llms.txt. Use this file to discover all available pages.

A MenuView component with an API compatible with @react-native-menu/menu. Supports both single-tap (default) and long-press (shouldOpenOnLongPress) triggers.

Under the hood this component wraps the platform-specific @expo/ui primitives:

If you need lower-level control, use those primitives directly.

Installation

Terminal
npx expo install @expo/ui

If you are installing this in an existing React Native app, make sure to install expo in your project.

Migrating from @react-native-menu/menu

  • Update the import from import { MenuView } from '@react-native-menu/menu' to import { MenuView } from '@expo/ui/community/menu'.
  • action.image on Android differs from upstream. @react-native-menu/menu expects a drawable resource name string (for example, 'ic_menu_add') that it resolves against android/app/src/main/res/drawable/. This drop-in does not resolve drawable resource names — pass an ImageSourcePropType instead (for example, require('@expo/material-symbols/edit.xml')). String values are accepted on iOS as SF Symbol names. Use Icon.select to define both per call site so the unused side tree-shakes out per platform.
  • title is rendered as a section header on iOS only; Android's Material DropdownMenu has no title slot.
  • On Android, MenuView wraps the trigger in its own Pressable to open the menu, so an onPress/onLongPress handler attached to a Pressable you pass as children won't fire — the outer wrapper claims the gesture. Move that handler into your onPressAction switch instead, or use the lower-level DropdownMenu primitive if you need to keep separate tap and long-press actions on the trigger.
  • The imperative ref.show() API is Android-only. SwiftUI Menu/ContextMenu have no programmatic open API, so on iOS the call is a no-op (with a one-time dev warning).
  • The following props from @react-native-menu/menu are not supported: themeVariant, hitSlop, isAnchoredToRight, subtitle, keepsMenuPresented, preferredElementSize, and state: 'mixed'.

Basic usage

MenuExample.tsx
import { Icon } from '@expo/ui'; import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } from 'react-native'; const editIcon = Icon.select({ ios: 'pencil', android: import('@expo/material-symbols/edit.xml'), }); const deleteIcon = Icon.select({ ios: 'trash', android: import('@expo/material-symbols/delete.xml'), }); export default function MenuExample() { return ( <MenuView actions={[ { id: 'edit', title: 'Edit', image: editIcon }, { id: 'delete', title: 'Delete', image: deleteIcon, attributes: { destructive: true } }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>Open menu</Text> </Pressable> </MenuView> ); }

Long-press (context menu)

Set shouldOpenOnLongPress to render as a context menu. On Android, the same controlled DropdownMenu opens from the Pressable's onLongPress instead of onPress. On iOS, this uses SwiftUI's ContextMenu and shows the trigger as a blurred preview.

LongPressMenuExample.tsx
import { Icon } from '@expo/ui'; import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } from 'react-native'; const copyIcon = Icon.select({ ios: 'doc.on.doc', android: import('@expo/material-symbols/content_copy.xml'), }); const shareIcon = Icon.select({ ios: 'square.and.arrow.up', android: import('@expo/material-symbols/share.xml'), }); export default function LongPressMenuExample() { return ( <MenuView shouldOpenOnLongPress actions={[ { id: 'copy', title: 'Copy', image: copyIcon }, { id: 'share', title: 'Share', image: shareIcon }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>Long-press me</Text> </Pressable> </MenuView> ); }

subactions renders nested actions as a submenu by default. Set displayInline: true on the parent to render the children as an inline section instead, which is useful for grouping. On Android, only the divider appears (Material's DropdownMenu has no section primitive). On iOS, the parent's title becomes the section header.

SubmenuExample.tsx
import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } from 'react-native'; export default function SubmenuExample() { return ( <MenuView actions={[ { id: 'rename', title: 'Rename' }, { id: 'sort', title: 'Sort by', subactions: [ { id: 'sort-name', title: 'Name' }, { id: 'sort-date', title: 'Date' }, { id: 'sort-size', title: 'Size' }, ], }, { id: 'share-section', title: 'Share', displayInline: true, subactions: [ { id: 'share-airdrop', title: 'AirDrop' }, { id: 'share-message', title: 'Message' }, ], }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>Open menu</Text> </Pressable> </MenuView> ); }

Toggle items with checkmarks

Set state to 'on' or 'off' to render an action as a togglable item with a leading checkmark when on. Selecting the action fires onPressAction and the caller is responsible for updating the state.

ToggleMenuExample.tsx
import { MenuView } from '@expo/ui/community/menu'; import { useState } from 'react'; import { Pressable, Text } from 'react-native'; export default function ToggleMenuExample() { const [pinned, setPinned] = useState(false); return ( <MenuView actions={[{ id: 'pin', title: 'Pin to top', state: pinned ? 'on' : 'off' }]} onPressAction={e => { if (e.nativeEvent.event === 'pin') setPinned(p => !p); }}> <Pressable> <Text>{pinned ? 'Pinned' : 'Not pinned'}</Text> </Pressable> </MenuView> ); }

API

import { MenuView } from '@expo/ui/community/menu';

Component

Android
iOS

Type: React.Element<MenuComponentProps & { ref: Ref<MenuComponentRef> }>

A drop-in replacement for @react-native-menu/menu's MenuView. Wrap any trigger view; long-pressing or tapping (per shouldOpenOnLongPress) shows a popup menu built from the actions tree.

  • On Android, renders via Compose's DropdownMenu anchored to a Pressable.
  • On iOS, renders via SwiftUI's Menu (tap) or ContextMenu (long-press).
  • On web, the trigger renders the trigger but actions do not fire; a one-time console.warn is emitted.

Props

actions

Android
iOS

The actions to display in the menu.

children

Android
iOS
Optional • Type: ReactNode

Trigger view. Long-pressing or tapping (per shouldOpenOnLongPress) opens the menu.

onCloseMenu

Android
Optional • Type: () => void

Callback invoked when the menu closes (either via dismissal or after an action fires).

On Android, fires from the controlled DropdownMenu's dismiss path. On iOS, SwiftUI Menu/ContextMenu do not expose a close hook in a way we can forward, so this is not fired there.

onOpenMenu

Android
Optional • Type: () => void

Callback invoked when the menu opens.

On Android, fires when the trigger's tap/long-press flips expanded to true. On iOS, SwiftUI Menu/ContextMenu do not expose an open hook, so this is not fired there.

onPressAction

Android
iOS
Optional • Type: (event: NativeActionEvent) => void

Callback invoked when a menu action is selected.

shouldOpenOnLongPress

Android
iOS
Optional • Type: boolean • Default: false

When true, the menu opens on long-press of the trigger instead of a single tap.

style

Android
iOS
Optional • Type: StyleProp<ViewStyle>

Style applied to the trigger wrapper.

testID

Android
iOS
Optional • Type: string

Test identifier passed through to the trigger view.

title

iOS
Optional • Type: string

Menu title shown at the top of the menu.

Types

Android
iOS

A single action inside a MenuView. Compatible with @react-native-menu/menu.

PropertyTypeDescription
attributes(optional)MenuAttributes

Visual/behavioral flags.

displayInline(optional)boolean

When true and subactions is present, renders the children as an inline section inside the parent menu (with this action's title as the section header on iOS).

id(optional)string

Identifier passed back via onPressAction.nativeEvent.event when this action is selected. Defaults to title if omitted.

image(optional)SFSymbol | ImageSourcePropType

Icon to render beside the action label.

  • When an SFSymbol name (e.g. 'trash'), rendered on iOS only. Not rendered on Android — pass an ImageSourcePropType instead to show an icon there.
  • When an ImageSourcePropType (e.g. require('./trash.xml') or { uri: '...' }), rendered on Android via Compose Icon. Ignored on iOS; SwiftUI menus only accept SF Symbol names for built-in Menu/Button labels.
imageColor(optional)ColorValue

Tint color applied to the action's icon.

Visually applied on Android via the leading Icon's tint. On iOS, the value is accepted but may not render: SwiftUI's Menu/ContextMenu draw their items via the system menu UI, which ignores per-item color modifiers.

state(optional)MenuState

Selection state. When 'on', the action renders a checkmark.

subactions(optional)MenuAction[]

Nested actions. Without displayInline, renders as a submenu; with displayInline: true, renders as an inline section.

titlestring

Action label shown in the menu.

titleColor(optional)ColorValue
Only for:
Android

Text color of the action label.

Android
iOS

Visual and behavioral attributes of a menu action. Compatible with @react-native-menu/menu.

PropertyTypeDescription
destructive(optional)boolean

Renders the action with a destructive style (red text/icon).

disabled(optional)boolean

Disables the action so it can't be activated.

hidden(optional)boolean

Hides the action from the menu.

Android
iOS

Imperative handle exposed by MenuView via ref. Compatible with @react-native-menu/menu's ref.show() API.

PropertyTypeDescription
show() => void
Only for:
Android

Programmatically open the menu.

On Android, opens the anchored DropdownMenu (equivalent to the user tapping the trigger). On iOS this is a no-op — SwiftUI Menu/ContextMenu have no programmatic open API; a one-time console.warn is emitted in development.

Android
iOS

Literal Type: string

Selection state for a menu action. 'on' renders a checkmark; 'off' doesn't.

Acceptable values are: 'on' | 'off'

NativeActionEvent

Android
iOS

Event payload delivered to onPressAction when an action is selected. Compatible with @react-native-menu/menu.

PropertyTypeDescription
nativeEvent{ event: string }
-