---
title: Menu
description: A SwiftUI Menu component for displaying dropdown menus.
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui'
packageName: '@expo/ui'
platforms: ['ios', 'tvos']
---

<AgentInstructions>

## Submitting Feedback

If this page contains errors, outdated information, or gaps that blocked you from completing a task, report it so the docs can be improved:

curl -X POST https://api.expo.dev/v2/feedback/docs-send -H 'Content-Type: application/json' -d '{"url":"/versions/latest/sdk/ui/swift-ui/menu/","feedback":"🤖 Agent feedback: <specific, actionable description>"}'

Only submit when you have something specific and actionable to report.

</AgentInstructions>

# Menu

A SwiftUI Menu component for displaying dropdown menus.
iOS, tvOS

> For the complete documentation index, see [llms.txt](/llms.txt). Use this file to discover all available pages.

Expo UI Menu matches the official SwiftUI [Menu API](https://developer.apple.com/documentation/swiftui/menu) and supports styling via the [`buttonStyle`](/versions/latest/sdk/ui/swift-ui/modifiers#buttonstylestyle) modifier. Menu opens on a single tap. For long-press interactions, use [`ContextMenu`](/versions/latest/sdk/ui/swift-ui/contextmenu) instead.

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

## Installation

```sh
npx expo install @expo/ui
```

If you are installing this in an [existing React Native app](/bare/overview), make sure to [install `expo`](/bare/installing-expo-modules) in your project.

## Usage

### Simple text label

```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

```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.

```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.

```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.

```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.

```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>
  );
}
```

### Glass menu

To create a menu with the iOS Liquid Glass appearance, use `buttonStyle('glass')` or `buttonStyle('glassProminent')` on the Menu component.

> **Important:** Do not apply the `glassEffect()` modifier to the Menu's label view to achieve a glass look. This causes a visual artifact where a rectangular halo briefly appears behind the trigger when the menu is dismissed. Always use `buttonStyle` instead, which correctly integrates with the Menu's dismiss animation.

```tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui';
import { buttonStyle } from '@expo/ui/swift-ui/modifiers';

export default function GlassMenuExample() {
  return (
    <Host matchContents>
      <Menu label="Glass Menu" systemImage="ellipsis.circle" modifiers={[buttonStyle('glass')]}>
        <Button label="Action 1" onPress={() => console.log('Action 1')} />
        <Button label="Action 2" onPress={() => console.log('Action 2')} />
      </Menu>
    </Host>
  );
}
```

For a more prominent glass effect, use `glassProminent`:

```tsx
import { Host, Menu, Button } from '@expo/ui/swift-ui';
import { buttonStyle } from '@expo/ui/swift-ui/modifiers';

export default function GlassProminentMenuExample() {
  return (
    <Host matchContents>
      <Menu
        label="Glass Prominent Menu"
        systemImage="slider.horizontal.3"
        modifiers={[buttonStyle('glassProminent')]}>
        <Button label="Settings" systemImage="gear" onPress={() => console.log('Settings')} />
        <Button
          label="Filter"
          systemImage="line.3.horizontal.decrease"
          onPress={() => console.log('Filter')}
        />
      </Menu>
    </Host>
  );
}
```

### With control group

Use a [`ControlGroup`](/versions/latest/sdk/ui/swift-ui/controlgroup) inside a menu to render a horizontal row of icon buttons, similar to the quick actions row in Apple Music or Safari menus.

```tsx
import { Host, Menu, ControlGroup, Button, Section, Divider } from '@expo/ui/swift-ui';

export default function MenuWithControlGroupExample() {
  return (
    <Host matchContents>
      <Menu label="Song Options" systemImage="ellipsis.circle">
        <ControlGroup>
          <Button systemImage="plus" label="Add" onPress={() => console.log('Add')} />
          <Button systemImage="star" label="Favorite" onPress={() => console.log('Favorite')} />
          <Button
            systemImage="square.and.arrow.up"
            label="Share"
            onPress={() => console.log('Share')}
          />
        </ControlGroup>
        <Section>
          <Button
            systemImage="text.badge.plus"
            label="Add to a Playlist"
            onPress={() => console.log('Add to Playlist')}
          />
          <Button
            systemImage="antenna.radiowaves.left.and.right"
            label="Create Station"
            onPress={() => console.log('Create Station')}
          />
        </Section>
        <Divider />
        <Button
          systemImage="hand.thumbsdown"
          label="Suggest Less"
          onPress={() => console.log('Suggest Less')}
        />
      </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.

```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

```tsx
import { Menu } from '@expo/ui/swift-ui';
```

## Component

### `Menu`

Supported platforms: iOS, tvOS.

Type: React.[Element](https://www.typescriptlang.org/docs/handbook/jsx.html#function-component)<[MenuProps](#menuprops)\>

Displays a dropdown menu when tapped.

Props for the `Menu` component.

MenuProps

### `children`

Supported platforms: iOS, tvOS.

Type: [ReactNode](https://reactnative.dev/docs/react-node)

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

### `label`

Supported platforms: 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](https://reactnative.dev/docs/react-node)

### `onPrimaryAction`

Supported platforms: 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`

Supported platforms: iOS, tvOS.

Optional • Type: `string`

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

#### Inherited Props

-   [CommonViewModifierProps](/versions/latest/sdk/ui/swift-ui/modifiers)
