Menu

A selectable list in a popover with nested submenus

Menu presents keyboard-navigable actions, selections, groups, and submenus in a popover or platform-native menu.

Features

  • Full keyboard navigation

  • Submenus, items, icons, images, checkboxes, and groups

  • Modal and non-modal modes

  • Platform-native menus on mobile

Menu displays a list of actions or options in a floating panel triggered by a button. It supports nested submenus, keyboard navigation, native platform menus, and automatically stacks above other content.

Installation

Menu is already installed in tamagui, or you can install it independently:

yarn add @tamagui/menu

For Expo 57 and React Native 0.86, use the Expo UI adapter:

yarn add @expo/ui

Then add the setup import at your app entry point:

import '@tamagui/native/setup-expo-ui-menu'

The Zeego adapter remains available for apps on compatible React Native versions:

yarn add @react-native-menu/menu react-native-ios-context-menu react-native-ios-utilities zeego sf-symbols-typescript
import '@tamagui/native/setup-zeego'

On native, Menu.Trigger coordinates with parent Tamagui pressables automatically. You should not need extra wrapper views or empty onPress handlers just to prevent a parent press: clause from activating.

Expo Router users: This import must run before expo-router/entry. Create an index.js at your project root that imports the setup first, then expo-router, and update your package.json main field to "index.js". See the Expo guide for details.

Anatomy

Import all parts and piece them together.

import { Menu } from 'tamagui' // or '@tamagui/menu'
export default () => (
<Menu>
<Menu.Trigger asChild>
<Button />
</Menu.Trigger>
<Menu.Portal zIndex={100}>
<Menu.Content>
<Menu.Item>
<Menu.ItemTitle>About Notes</Menu.ItemTitle>
</Menu.Item>
<Menu.Item>
<Menu.ItemTitle>Settings</Menu.ItemTitle>
</Menu.Item>
{/* when title is nested inside a React element then you need to use `textValue` */}
<Menu.Item textValue="Calendar">
<Menu.ItemTitle>
<Text>Calendar</Text>
</Menu.ItemTitle>
<Menu.ItemIcon>
<Calendar color="gray" size="1" />
</Menu.ItemIcon>
</Menu.Item>
<Menu.Separator />
<Menu.Sub>
<Menu.SubTrigger>
<Menu.ItemTitle>Actions</Menu.ItemTitle>
</Menu.SubTrigger>
<Menu.Portal zIndex={200}>
<Menu.SubContent>
<Menu.Label fontSize={'1'}>Note settings</Menu.Label>
<Menu.Item onSelect={onSelect} key="create-note">
<Menu.ItemTitle>Create note</Menu.ItemTitle>
</Menu.Item>
<Menu.Item onSelect={onSelect} key="delete-all">
<Menu.ItemTitle>Delete all notes</Menu.ItemTitle>
</Menu.Item>
<Menu.Item onSelect={onSelect} key="sync-all">
<Menu.ItemTitle>Sync notes</Menu.ItemTitle>
</Menu.Item>
</Menu.SubContent>
</Menu.Portal>
</Menu.Sub>
</Menu.Content>
</Menu.Portal>
</Menu>
)

API reference

Contains every component for the Menu.

Props

  • children (required)

    React.ReactNode

    Menu parts: Trigger, Portal, Content, Items, etc.

  • placement

    Placement

    Where the menu appears relative to the trigger. Options: 'top' | 'right' | 'bottom' | 'left' with optional '-start' | '-end' alignment.

  • open

    boolean

    Controlled open state for the menu.

  • defaultOpen

    boolean

    Initial open state when uncontrolled.

  • onOpenChange

    (open: boolean) => void

    Called when the menu opens or closes.

  • onOpenWillChange

    (open: boolean) => void

    Called before the open/close animation begins.

  • modal

    boolean

    Default: 

    true

    When true, traps focus inside the menu and blocks outside scroll/interactions.

  • stayInFrame

    ShiftProps | boolean

    Default: 

    { padding: 10 }

    Shifts the menu horizontally to stay within viewport bounds. Pass an object to customize shift behavior (mainAxis, crossAxis, padding).

  • allowFlip

    FlipProps | boolean

    Default: 

    { padding: 10 }

    Flips the menu to the opposite side if there is insufficient space.

  • offset

    OffsetOptions

    Default: 

    10

    Distance between the menu and its trigger.

  • resize

    boolean

    Default: 

    true

    Constrains the menu to fit within available viewport space. When enabled, use Menu.ScrollView inside Content for scrollable overflow.

  • unstyled

    boolean

    Removes all default Tamagui styles.

  • Required for rendering the menu content.

    Props

  • zIndex

    number

    Stacking order of the portal layer.

  • children (required)

    React.ReactNode

    Content to render inside the portal.

  • forceMount

    true

    Forces the portal to stay mounted, useful for controlling animations.

  • Wrap related menu triggers in Menu.TriggerGroup to make a web menubar. Hovering an enabled sibling switches menus once a menu in that group is open. Hovering an idle group does not open a menu. Each panel opens at its own trigger.

    <Menu.TriggerGroup gap="2" aria-label="Application">
    <Menu placement="bottom-start">
    <Menu.Trigger asChild><Button>File</Button></Menu.Trigger>
    <Menu.Portal>
    <Menu.Content>
    <Menu.Item textValue="New"><Menu.ItemTitle>New</Menu.ItemTitle></Menu.Item>
    </Menu.Content>
    </Menu.Portal>
    </Menu>
    <Menu placement="bottom-start">
    <Menu.Trigger asChild><Button>Edit</Button></Menu.Trigger>
    <Menu.Portal>
    <Menu.Content>
    <Menu.Item textValue="Undo"><Menu.ItemTitle>Undo</Menu.ItemTitle></Menu.Item>
    </Menu.Content>
    </Menu.Portal>
    </Menu>
    </Menu.TriggerGroup>

    The container accepts Tamagui Props and dir="ltr" | "rtl". It lays out triggers in a row. Left and right arrow keys move between enabled triggers and wrap at either end. While a menu is open, they switch to the next menu and focus its first item. Submenu opening and closing keys keep their usual behavior. Escape closes the menu and returns focus to its active trigger.

    You can also place a trigger group inside one Menu whose triggers share a single content panel. Use trigger onMouseEnter and onFocus handlers to prepare the corresponding contents. Groups are independent, and grouped web menus are non-modal so sibling triggers remain reachable. Native platform menus keep their platform interactions. Menu.Group continues to group items inside a menu panel.

    Trigger element for the menu. Accepts Tamagui Props.

    Props

  • action

    press|longPress

    Default: 

    press

    Works with the native prop and accepts 'press' or 'longPress'. The default is 'press' for Menu.

  • Contains the content of the menu.

    Props

  • children (required)

    React.ReactNode

    Menu items, groups, labels, separators, and submenus.

  • loop

    boolean

    Default: 

    false

    Whether keyboard navigation wraps from last to first item.

  • forceMount

    true

    Forces the content to stay mounted, useful for controlling animations.

  • onCloseAutoFocus

    (event: Event) => void

    Called when focus returns to the trigger after closing.

  • onEscapeKeyDown

    (event: KeyboardEvent) => void

    Called when the escape key is pressed. Can be prevented.

  • onPointerDownOutside

    (event: PointerEvent) => void

    Called when a pointer event occurs outside the content.

  • onInteractOutside

    (event: Event) => void

    Called when any interaction occurs outside the content.

  • A selectable menu item that triggers an action when selected.

    Props

  • key (required)

    string

    Unique identifier for the item.

  • disabled

    boolean

    Default: 

    false

    Prevents interaction and dims the item.

  • destructive

    boolean

    Renders the item in red on iOS to indicate a dangerous action (e.g. delete). No effect on web.

  • hidden

    boolean

    Hides the item from the menu.

  • onSelect

    (event?: Event) => void

    Called when the item is selected via click or keyboard.

  • onFocus

    () => void

    Called when the item receives focus.

  • onBlur

    () => void

    Called when the item loses focus.

  • textValue

    string

    Text used for typeahead and native menus. Required when ItemTitle contains a React node instead of a string.

  • Renders the title of the menu item.

    Props

  • children (required)

    string | React.ReactNode

    The title text or element to display.

  • You can directly pass a text node to the ItemTitle. However, if you use a nested React node like <Text>, you need to pass textValue to the <Item> so that it works with native menus.

    A component to render an icon. For non-native menus, you can pass an icon component. For native menus, you can pass platform-specific icons to the android and ios props.

    On iOS, it renders the native SF Symbols icons.

    Props

  • children

    React.ReactNode

    Fallback icon for web when native icons are not available.

  • ios

    object

    SF Symbol configuration: name, weight, scale, hierarchicalColor, paletteColors.

  • android

    object

    Android resource drawable name.

  • <Menu.ItemIcon ios={{ name: '0.circle.fill', // required pointSize: 5, weight: 'semibold', scale: 'medium', // can also be a color string. Requires iOS 15+ hierarchicalColor: { dark: 'blue', light: 'green', }, // alternative to hierarchical color. Requires iOS 15+ paletteColors: [ { dark: 'blue', light: 'green', }, ], }} >
    <CircleIcon />
    </Menu.ItemIcon>

    A component to render an item image. For native menus, it only works on iOS. It takes the same props as @tamagui/image.

    A component to render a subtitle for the menu item. For native menus, it only works on iOS.

    Props

  • children (required)

    string

    The subtitle text to display below the title.

  • A component that groups multiple menu items together.

    Props

  • children (required)

    React.ReactNode

    Menu items to group together.

  • A menu item with a checkbox that can be toggled on and off.

    Props

  • key (required)

    string

    Unique identifier for the checkbox item.

  • disabled

    boolean

    Default: 

    false

    Prevents interaction and dims the item.

  • destructive

    boolean

    Renders the item in red on iOS to indicate a dangerous action. No effect on web.

  • hidden

    boolean

    Hides the item from the menu.

  • onFocus

    () => void

    Called when the item receives focus.

  • onBlur

    () => void

    Called when the item loses focus.

  • textValue

    string

    Text for native menus. Required when ItemTitle contains a React node.

  • value

    'on' | 'off' | 'mixed'

    Controlled checked state for native menus.

  • onValueChange

    (state, prevState) => void

    Called when checked state changes on native menus.

  • checked

    boolean

    Controlled checked state for web menus.

  • onCheckedChange

    (checked: boolean) => void

    Called when checked state changes on web.

  • Groups a set of Menu.RadioItem and manages the selected value. Works on web and native. On native, the group is rendered as a native menu group and each RadioItem is mapped onto the OS checkmark state so exactly one item shows as selected at a time.

    const [color, setColor] = React.useState('blue')
    <Menu.RadioGroup value={color} onValueChange={setColor}>
    <Menu.RadioItem key="red" value="red">
    <Menu.ItemIndicator>
    <Check size={16} />
    </Menu.ItemIndicator>
    <Menu.ItemTitle>Red</Menu.ItemTitle>
    </Menu.RadioItem>
    <Menu.RadioItem key="blue" value="blue">
    <Menu.ItemIndicator>
    <Check size={16} />
    </Menu.ItemIndicator>
    <Menu.ItemTitle>Blue</Menu.ItemTitle>
    </Menu.RadioItem>
    </Menu.RadioGroup>

    Props

  • children (required)

    React.ReactNode

    One or more Menu.RadioItem components.

  • value

    string

    The controlled value of the currently selected item.

  • onValueChange

    (value: string) => void

    Called when the selected value changes.

  • An item that can be selected inside a Menu.RadioGroup. Render a Menu.ItemIndicator inside to show a checkmark for the active value on web; on native the OS draws its own checkmark and the ItemIndicator child is dropped automatically.

    Props

  • key (required)

    string

    Unique identifier for the radio item.

  • value (required)

    string

    The value of this item. When it matches the parent RadioGroup value, the item is selected.

  • disabled

    boolean

    Default: 

    false

    Prevents interaction and dims the item.

  • hidden

    boolean

    Hides the item from the menu.

  • onFocus

    () => void

    Called when the item receives focus.

  • onBlur

    () => void

    Called when the item loses focus.

  • Use inside Menu.CheckboxItem or Menu.RadioItem to indicate when an item is checked. This allows you to conditionally render a checkmark.

    <Menu.ItemIndicator>
    <CheckmarkIcon /> {/* This does not work with the native prop. */}
    </Menu.ItemIndicator>

    Props

  • children

    React.ReactNode

    Custom checkmark icon. Only works on web.

  • forceMount

    true

    Forces the indicator to stay mounted for animation control.

  • Renders a non-focusable label for a group of items. On native menus, only one label is supported per menu and submenu.

    Props

  • children (required)

    string

    The label text.

  • textValue

    string

    Text for native menus when children is a React node.

  • Renders an arrow pointing to the trigger.

    Props

  • size

    number | SizeToken

    Width and height of the arrow.

  • unstyled

    boolean

    Removes default arrow styles.

  • Renders a visual divider between menu items. Web only.

    A container for nested submenu components.

    Props

  • children (required)

    React.ReactNode

    SubTrigger, Portal, and SubContent components.

  • open

    boolean

    Controlled open state for the submenu.

  • onOpenChange

    (open: boolean) => void

    Called when the submenu opens or closes.

  • Renders the content of a submenu. Same props as Menu.Content.

    A menu item that opens a submenu on hover or focus. Accepts the same props as Menu.Item.

    A scrollable container for menu items. Use this inside Menu.Content when you have many items that may overflow. The menu automatically constrains to available viewport space (via resize prop), and ScrollView handles the overflow. Scrollbars are hidden by default.

    <Menu.Content>
    <Menu.ScrollView>
    {/* Many menu items */}
    </Menu.ScrollView>
    </Menu.Content>

    Styling

    Item highlight behavior

    Menu items use the focus condition for highlighting. Write it as a typed object key or a focus: string clause. Focusing the highlighted item keeps mouse and keyboard navigation in sync, with only one item highlighted at a time.

    When you hover over an item, it receives focus, which activates the focus: clause. When you use arrow keys to navigate, focus moves to the new item, removing the highlight from the previous one.

    // default highlight follows focus
    <Menu.Item>
    <Menu.ItemTitle>Settings</Menu.ItemTitle>
    </Menu.Item>
    // customize the focus highlight
    <Menu.Item backgroundColor="focus:blue-5">
    <Menu.ItemTitle>Custom Highlight</Menu.ItemTitle>
    </Menu.Item>

    Avoid using a hover: background clause on Menu.Item. This can cause “double highlighting” when switching between mouse and keyboard, where both the hovered item and the focused item appear highlighted simultaneously.