Reference version

This is documentation for the next SDK version. For up-to-date documentation, see the latest version (SDK 54).

Menu

A SwiftUI Menu component for displaying dropdown menus.

iOS
tvOS

Expo UI Menu matches the official SwiftUI Menu API and supports styling via the buttonStyle modifier.

Note: On tvOS, Menu requires tvOS 17.0 or later.

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.

Usage

Simple text label

SimpleMenuExample.tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui'; export default function SimpleMenuExample() { return ( <Host matchContents> <Menu label="Options"> <Button label="Option 1" onPress={() => console.log('Option 1')} /> <Button label="Option 2" onPress={() => console.log('Option 2')} /> <Button label="Option 3" onPress={() => console.log('Option 3')} /> </Menu> </Host> ); }

Text label with SF Symbol

MenuWithIconExample.tsx
import { Host, Menu, Button, Divider } from '@expo/ui/swift-ui'; export default function MenuWithIconExample() { return ( <Host matchContents> <Menu label="More" systemImage="ellipsis.circle"> <Button label="Settings" systemImage="gear" onPress={() => console.log('Settings')} /> <Button label="Profile" systemImage="person" onPress={() => console.log('Profile')} /> <Divider /> <Button label="Delete" role="destructive" systemImage="trash" onPress={() => console.log('Delete')} /> </Menu> </Host> ); }

Custom label

You can pass a React node as the label for custom styling.

CustomLabelMenuExample.tsx
import { Host, Menu, Button, Text } from '@expo/ui/swift-ui'; export default function CustomLabelMenuExample() { return ( <Host matchContents> <Menu label={<Text color="accentColor">Custom Label</Text>}> <Button label="Action 1" onPress={() => console.log('Action 1')} /> <Button label="Action 2" onPress={() => console.log('Action 2')} /> </Menu> </Host> ); }

Nested menu

Menus can be nested to create submenus.

NestedMenuExample.tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui'; export default function NestedMenuExample() { return ( <Host matchContents> <Menu label="Main Menu"> <Button label="Item 1" onPress={() => console.log('Item 1')} /> <Menu label="Submenu"> <Button label="Sub Item 1" onPress={() => console.log('Sub Item 1')} /> <Button label="Sub Item 2" onPress={() => console.log('Sub Item 2')} /> </Menu> <Button label="Item 2" onPress={() => console.log('Item 2')} /> </Menu> </Host> ); }

With primary action

When onPrimaryAction is provided, a single tap triggers the primary action while a long-press shows the menu.

PrimaryActionMenuExample.tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui'; export default function PrimaryActionMenuExample() { return ( <Host matchContents> <Menu label="Tap or Hold" systemImage="play.circle" onPrimaryAction={() => console.log('Primary action triggered!')}> <Button label="Menu Item 1" onPress={() => console.log('Menu Item 1')} /> <Button label="Menu Item 2" onPress={() => console.log('Menu Item 2')} /> <Button label="Menu Item 3" onPress={() => console.log('Menu Item 3')} /> </Menu> </Host> ); }

Styling with modifiers

You can use the buttonStyle modifier to change the appearance of the menu trigger.

StyledMenuExample.tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui'; import { buttonStyle } from '@expo/ui/swift-ui/modifiers'; export default function StyledMenuExample() { return ( <Host matchContents> <Menu label="Styled Menu" modifiers={[buttonStyle('borderedProminent')]}> <Button label="Styled Action 1" onPress={() => console.log('Styled 1')} /> <Button label="Styled Action 2" onPress={() => console.log('Styled 2')} /> </Menu> </Host> ); }

Icon only menu button

Use the labelStyle('iconOnly') modifier to display only the icon without the label text. The label prop should still be provided for accessibility purposes.

IconOnlyMenuExample.tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui'; import { labelStyle } from '@expo/ui/swift-ui/modifiers'; export default function IconOnlyMenuExample() { return ( <Host matchContents> <Menu label="Icon Only Button" systemImage="gear" modifiers={[labelStyle('iconOnly')]}> <Button label="Menu Item 1" onPress={() => console.log('Menu Item 1')} /> <Button label="Menu Item 2" onPress={() => console.log('Menu Item 2')} /> <Button label="Menu Item 3" onPress={() => console.log('Menu Item 3')} /> </Menu> </Host> ); }

API

Component

iOS
tvOS

Type: React.Element<MenuProps>

Displays a dropdown menu when tapped.

Props for the Menu component.

MenuProps

children

iOS
tvOS
Type: ReactNode

The menu's content items, which are shown when the menu is opened. Can contain Button, Switch, Picker, Section, Divider, or nested Menu components.

label

iOS
tvOS
Literal type: union

The label for the menu trigger. Can be a string for simple text labels, or a ReactNode for custom label content.

Acceptable values are: string | ReactNode

onPrimaryAction

iOS
tvOS
Optional • Type: () => void

A callback that is invoked when the user taps the menu label. When provided, a single tap triggers this action, while a long-press shows the menu. When not provided, a single tap shows the menu.

systemImage

iOS
tvOS
Optional • Type: string

An SF Symbol name to display alongside the label. Only used when label is a string.