MenuNew

A floating context menu with positioning, selection groups, and multiple presentation modes.

Import

import { Menu } from 'heroui-native';

Anatomy

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover">
      <Menu.Close />
      <Menu.Label>...</Menu.Label>
      <Menu.Group>
        <Menu.Item>
          <Menu.ItemIndicator />
          <Menu.ItemTitle>...</Menu.ItemTitle>
          <Menu.ItemDescription>...</Menu.ItemDescription>
        </Menu.Item>
      </Menu.Group>
    </Menu.Content>
  </Menu.Portal>
</Menu>
  • Menu: Main container that manages open/close state, positioning, and provides context to child components.
  • Menu.Trigger: Clickable element that toggles the menu visibility.
  • Menu.Portal: Renders menu content in a portal layer above other content.
  • Menu.Overlay: Optional background overlay to capture outside clicks and close the menu.
  • Menu.Content: Container for menu content with two presentation modes: floating popover with positioning and collision detection, or bottom sheet modal.
  • Menu.Close: Close button that dismisses the menu when pressed.
  • Menu.Label: Non-interactive section heading text within the menu.
  • Menu.Group: Groups menu items with optional selection state (none, single, multiple).
  • Menu.Item: Pressable menu item with animated press feedback. Standalone or within a Group for selection.
  • Menu.ItemTitle: Primary label text for a menu item.
  • Menu.ItemDescription: Secondary description text for a menu item.
  • Menu.ItemIndicator: Visual selection indicator (checkmark or dot) for a menu item.

Usage

Basic Usage

The Menu component uses compound parts to create a floating context menu.

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" width={220}>
      <Menu.Item>
        <Menu.ItemTitle>View Profile</Menu.ItemTitle>
      </Menu.Item>
      <Menu.Item>
        <Menu.ItemTitle>Settings</Menu.ItemTitle>
      </Menu.Item>
    </Menu.Content>
  </Menu.Portal>
</Menu>

With Item Descriptions

Add secondary description text to menu items alongside titles.

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" width={260}>
      <Menu.Item className="items-start">
        <View className="flex-1">
          <Menu.ItemTitle>New file</Menu.ItemTitle>
          <Menu.ItemDescription>Create a new file</Menu.ItemDescription>
        </View>
      </Menu.Item>
      <Menu.Item className="items-start">
        <View className="flex-1">
          <Menu.ItemTitle>Copy link</Menu.ItemTitle>
          <Menu.ItemDescription>Copy the file link</Menu.ItemDescription>
        </View>
      </Menu.Item>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Single Selection

Use Menu.Group with selectionMode="single" to allow one selected item at a time.

const [theme, setTheme] = useState<Set<MenuKey>>(() => new Set(['system']));

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" width={180}>
      <Menu.Label>Appearance</Menu.Label>
      <Menu.Group
        selectionMode="single"
        selectedKeys={theme}
        onSelectionChange={setTheme}
      >
        <Menu.Item id="light">
          <Menu.ItemIndicator />
          <Menu.ItemTitle>Light</Menu.ItemTitle>
        </Menu.Item>
        <Menu.Item id="dark">
          <Menu.ItemIndicator />
          <Menu.ItemTitle>Dark</Menu.ItemTitle>
        </Menu.Item>
        <Menu.Item id="system">
          <Menu.ItemIndicator />
          <Menu.ItemTitle>System</Menu.ItemTitle>
        </Menu.Item>
      </Menu.Group>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Multiple Selection

Use selectionMode="multiple" to allow selecting multiple items simultaneously.

const [textStyles, setTextStyles] = useState<Set<MenuKey>>(
  () => new Set(['bold', 'italic'])
);

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" width={250}>
      <Menu.Label>Text Style</Menu.Label>
      <Menu.Group
        selectionMode="multiple"
        selectedKeys={textStyles}
        onSelectionChange={setTextStyles}
      >
        <Menu.Item id="bold">
          <Menu.ItemIndicator />
          <Menu.ItemTitle>Bold</Menu.ItemTitle>
        </Menu.Item>
        <Menu.Item id="italic">
          <Menu.ItemIndicator />
          <Menu.ItemTitle>Italic</Menu.ItemTitle>
        </Menu.Item>
        <Menu.Item id="underline">
          <Menu.ItemIndicator />
          <Menu.ItemTitle>Underline</Menu.ItemTitle>
        </Menu.Item>
      </Menu.Group>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Danger Variant

Use variant="danger" on a menu item for destructive actions.

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" width={220}>
      <Menu.Item>
        <Menu.ItemTitle>Edit</Menu.ItemTitle>
      </Menu.Item>
      <Menu.Item variant="danger">
        <Menu.ItemTitle>Delete</Menu.ItemTitle>
      </Menu.Item>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Placements

Control where the menu appears relative to the trigger.

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" placement="right" width={200}>
      <Menu.Item>
        <Menu.ItemTitle>Option A</Menu.ItemTitle>
      </Menu.Item>
      <Menu.Item>
        <Menu.ItemTitle>Option B</Menu.ItemTitle>
      </Menu.Item>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Bottom Sheet Presentation

Use presentation="bottom-sheet" to display menu content as a bottom sheet modal.

<Menu presentation="bottom-sheet">
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="bottom-sheet">
      <Menu.Item>
        <Menu.ItemTitle>Option A</Menu.ItemTitle>
      </Menu.Item>
      <Menu.Item>
        <Menu.ItemTitle>Option B</Menu.ItemTitle>
      </Menu.Item>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Dot Indicator

Use variant="dot" on Menu.ItemIndicator to show a filled circle instead of a checkmark.

<Menu>
  <Menu.Trigger>...</Menu.Trigger>
  <Menu.Portal>
    <Menu.Overlay />
    <Menu.Content presentation="popover" width={180}>
      <Menu.Group
        selectionMode="single"
        selectedKeys={alignment}
        onSelectionChange={setAlignment}
      >
        <Menu.Item id="left">
          <Menu.ItemIndicator variant="dot" />
          <Menu.ItemTitle>Left</Menu.ItemTitle>
        </Menu.Item>
        <Menu.Item id="center">
          <Menu.ItemIndicator variant="dot" />
          <Menu.ItemTitle>Center</Menu.ItemTitle>
        </Menu.Item>
        <Menu.Item id="right">
          <Menu.ItemIndicator variant="dot" />
          <Menu.ItemTitle>Right</Menu.ItemTitle>
        </Menu.Item>
      </Menu.Group>
    </Menu.Content>
  </Menu.Portal>
</Menu>

Example

import type { MenuKey } from 'heroui-native';
import { Button, Menu, Separator } from 'heroui-native';
import { useState } from 'react';
import { Text, View } from 'react-native';

export default function MenuExample() {
  const [textStyles, setTextStyles] = useState<Set<MenuKey>>(
    () => new Set(['bold', 'italic'])
  );
  const [alignment, setAlignment] = useState<Set<MenuKey>>(
    () => new Set(['left'])
  );

  return (
    <Menu>
      <Menu.Trigger asChild>
        <Button variant="secondary">Styles</Button>
      </Menu.Trigger>
      <Menu.Portal>
        <Menu.Overlay />
        <Menu.Content presentation="popover" width={250}>
          <Menu.Label className="mb-1">Text Style</Menu.Label>
          <Menu.Group
            selectionMode="multiple"
            selectedKeys={textStyles}
            onSelectionChange={setTextStyles}
          >
            <Menu.Item id="bold">
              <Menu.ItemIndicator />
              <Menu.ItemTitle>Bold</Menu.ItemTitle>
              <Text className="text-sm text-muted">⌘ B</Text>
            </Menu.Item>
            <Menu.Item id="italic">
              <Menu.ItemIndicator />
              <Menu.ItemTitle>Italic</Menu.ItemTitle>
              <Text className="text-sm text-muted">⌘ I</Text>
            </Menu.Item>
            <Menu.Item id="underline">
              <Menu.ItemIndicator />
              <Menu.ItemTitle>Underline</Menu.ItemTitle>
              <Text className="text-sm text-muted">⌘ U</Text>
            </Menu.Item>
          </Menu.Group>
          <Separator className="mx-2 my-2 opacity-75" />
          <Menu.Label className="mb-1">Text Alignment</Menu.Label>
          <Menu.Group
            selectionMode="single"
            selectedKeys={alignment}
            onSelectionChange={setAlignment}
          >
            <Menu.Item id="left">
              <Menu.ItemIndicator variant="dot" />
              <Menu.ItemTitle>Left</Menu.ItemTitle>
            </Menu.Item>
            <Menu.Item id="center">
              <Menu.ItemIndicator variant="dot" />
              <Menu.ItemTitle>Center</Menu.ItemTitle>
            </Menu.Item>
            <Menu.Item id="right">
              <Menu.ItemIndicator variant="dot" />
              <Menu.ItemTitle>Right</Menu.ItemTitle>
            </Menu.Item>
          </Menu.Group>
        </Menu.Content>
      </Menu.Portal>
    </Menu>
  );
}

You can find more examples in the GitHub repository.

API Reference

proptypedefaultdescription
childrenReact.ReactNode-The content of the menu
presentation'popover' | 'bottom-sheet''popover'Presentation mode for the menu content
isOpenboolean-Controlled open state of the menu
isDefaultOpenboolean-Open state when initially rendered (uncontrolled)
isDisabledboolean-Whether the menu is disabled
animationMenuRootAnimation-Animation configuration for menu root
onOpenChange(open: boolean) => void-Callback fired when the menu open state changes
...ViewPropsViewProps-All standard React Native View props are supported

Animation configuration for menu root component. Can be:

  • "disable-all": Disable all animations including children
  • true or undefined: Use default animations
proptypedefaultdescription
childrenReact.ReactNode-The trigger element content
classNamestring-Additional CSS class for the trigger
isDisabledbooleanfalseWhether the trigger is disabled
asChildboolean-Render as child element using Slot pattern
...PressablePropsPressableProps-All standard React Native Pressable props are supported
proptypedefaultdescription
childrenReact.ReactNode-The portal content
classNamestring-Additional CSS class for the portal container
disableFullWindowOverlaybooleanfalseUse a regular View instead of FullWindowOverlay on iOS
hostNamestring-Optional name of the host element for the portal
forceMountboolean-Force mount the portal regardless of open state
proptypedefaultdescription
classNamestring-Additional CSS class for the overlay
closeOnPressbooleantrueWhether to close the menu when the overlay is pressed
animationMenuOverlayAnimation-Animation configuration for overlay
isAnimatedStyleActivebooleantrueWhether animated styles (react-native-reanimated) are active
forceMountboolean-Force mount the overlay regardless of open state
...PressablePropsPressableProps-All standard React Native Pressable props are supported

Animation configuration for menu overlay component. Can be:

  • false or "disabled": Disable all animations
  • true or undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
state'disabled' | boolean-Disable animations while customizing properties
opacity.entering.valueEntryOrExitLayoutTypeFadeIn.duration(200)Custom entering animation for overlay
opacity.exiting.valueEntryOrExitLayoutTypeFadeOut.duration(150)Custom exiting animation for overlay

Props when presentation="popover".

proptypedefaultdescription
childrenReact.ReactNode-The menu content
presentation'popover'-Presentation mode (must match Menu root)
placement'top' | 'bottom' | 'left' | 'right''bottom'Where the menu appears relative to the trigger
align'start' | 'center' | 'end''center'Alignment of the menu relative to the trigger
avoidCollisionsbooleantrueWhether to reposition to avoid screen edges
offsetnumber9Distance from the trigger element in pixels
alignOffsetnumber0Offset along the alignment axis in pixels
width'content-fit' | 'trigger' | 'full' | number'content-fit'Content width sizing strategy
classNamestring-Additional CSS class for the content container
animationMenuContentAnimation-Animation configuration for content
...ViewPropsViewProps-All standard React Native View props are supported

Animation configuration for menu popover content component. Can be:

  • false or "disabled": Disable all animations
  • true or undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
state'disabled' | boolean-Disable animations while customizing properties
entering.valueEntryOrExitLayoutTypeScale + fade entering animationCustom entering animation for content
exiting.valueEntryOrExitLayoutTypeScale + fade exiting animationCustom exiting animation for content

Props when presentation="bottom-sheet". Extends @gorhom/bottom-sheet BottomSheet props.

proptypedefaultdescription
childrenReact.ReactNode-The bottom sheet content
presentation'bottom-sheet'-Presentation mode (must match Menu root)
classNamestring-Additional CSS class for the bottom sheet
backgroundClassNamestring-Additional CSS class for the background
handleIndicatorClassNamestring-Additional CSS class for the handle indicator
contentContainerClassNamestring-Additional CSS class for the content container
contentContainerPropsOmit<BottomSheetViewProps, 'children'>-Props for the content container
animationAnimationDisabled-Set to false or "disabled" to disable animations
...BottomSheetPropsPartial<BottomSheetProps>-All @gorhom/bottom-sheet props are supported

Extends CloseButtonProps. Automatically closes the menu when pressed.

proptypedefaultdescription
iconPropsCloseButtonIconProps-Props for customizing the close icon
...ButtonPropsButtonRootProps-All Button root props are supported
proptypedefaultdescription
childrenReact.ReactNode-The group content (Menu.Item elements)
selectionMode'none' | 'single' | 'multiple''none'The type of selection allowed in the group
selectedKeysIterable<MenuKey>-Currently selected keys (controlled)
defaultSelectedKeysIterable<MenuKey>-Initially selected keys (uncontrolled)
isDisabledbooleanfalseWhether the entire group is disabled
disabledKeysIterable<MenuKey>-Keys of items that should be disabled
shouldCloseOnSelectboolean-Whether selecting an item should close the menu
classNamestring-Additional CSS class for the group container
onSelectionChange(keys: Set<MenuKey>) => void-Callback fired when the selection changes
...ViewPropsViewProps-All standard React Native View props are supported
proptypedefaultdescription
childrenReact.ReactNode-The label text content
classNamestring-Additional CSS class for the label
...TextPropsTextProps-All standard React Native Text props are supported
proptypedefaultdescription
childrenReact.ReactNode | ((props: MenuItemRenderProps) => ReactNode)-Child elements or a render function
idMenuKey-Unique identifier, required when inside a Menu.Group
variant'default' | 'danger''default'Visual variant of the menu item
isDisabledbooleanfalseWhether the item is disabled
isSelectedboolean-Controlled selected state for standalone items
shouldCloseOnSelectbooleantrueWhether pressing this item should close the menu
classNamestring-Additional CSS class for the item
animationMenuItemAnimation-Animation configuration for press feedback
isAnimatedStyleActivebooleantrueWhether animated styles (react-native-reanimated) are active
onSelectedChange(selected: boolean) => void-Callback when standalone item's selected state changes
...PressablePropsPressableProps-All standard React Native Pressable props are supported

Props passed to the render function when children is a function.

proptypedescription
isSelectedbooleanWhether this item is currently selected
isDisabledbooleanWhether the item is disabled
isPressedSharedValue<boolean>Whether the item is currently pressed
variant'default' | 'danger'Visual variant of the item

Animation configuration for menu item press feedback. Can be:

  • false or "disabled": Disable all item animations
  • true or undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
scale.valuenumber0.98Scale value when pressed
scale.timingConfigWithTimingConfig{ duration: 150 }Spring animation configuration for scale
backgroundColor.valuestringuseThemeColor('default')Background color shown while pressed
backgroundColor.timingConfigWithTimingConfig{ duration: 150 }Animation timing for background color
proptypedefaultdescription
childrenReact.ReactNode-The title text content
classNamestring-Additional CSS class for the item title
...TextPropsTextProps-All standard React Native Text props are supported
proptypedefaultdescription
childrenReact.ReactNode-The description text content
classNamestring-Additional CSS class for the item description
...TextPropsTextProps-All standard React Native Text props are supported
proptypedefaultdescription
childrenReact.ReactNode-Custom indicator content, defaults to checkmark or dot
variant'checkmark' | 'dot''checkmark'Visual variant of the indicator
iconPropsMenuItemIndicatorIconProps-Icon configuration (checkmark variant)
forceMountbooleantrueForce mount the indicator regardless of selected state
classNamestring-Additional CSS class for the item indicator
...ViewPropsViewProps-All standard React Native View props are supported
proptypedefaultdescription
sizenumber16Size of the indicator icon (8 for dot variant)
colorstringmutedColor of the indicator icon

Hooks

useMenu

Hook to access the menu root context. Must be used within a Menu component.

import { useMenu } from 'heroui-native';

const { isOpen, onOpenChange, presentation, isDisabled } = useMenu();

Returns

propertytypedescription
isOpenbooleanWhether the menu is currently open
onOpenChange(open: boolean) => voidCallback to change the open state
presentation'popover' | 'bottom-sheet'Current presentation mode
isDisabledboolean | undefinedWhether the menu is disabled
nativeIDstringUnique identifier for the menu instance

useMenuItem

Hook to access the menu item context. Must be used within a Menu.Item component.

import { useMenuItem } from 'heroui-native';

const { id, isSelected, isDisabled, variant } = useMenuItem();

Returns

propertytypedescription
idMenuKey | undefinedItem identifier
isSelectedbooleanWhether the item is currently selected
isDisabledbooleanWhether the item is disabled
variant'default' | 'danger'Visual variant of the item

useMenuAnimation

Hook to access the menu animation context. Must be used within a Menu component.

import { useMenuAnimation } from 'heroui-native';

const { progress, isDragging } = useMenuAnimation();

Returns

propertytypedescription
progressSharedValue<number>Animation progress shared value (0=idle, 1=open, 2=close)
isDraggingSharedValue<boolean>Whether the bottom sheet is currently being dragged

On this page