Pre-alphaThe registry and the CLI are not published yet.Roadmap

Menu

A menu that hangs from a button, modeled on iOS.

Library

Copy

Share

Favorite

Delete

Installation

npx axiom add menu
pnpm dlx axiom add menu
yarn dlx axiom add menu
bun x axiom add menu

Also copies: portal, overlay, use-overlay-back-handler, which closes the menu on a back action. See navigation. And overlay-stack: a menu opened from a BottomSheet takes the back action and the touches from the sheet, without scaling it.

Installs: react-native-reanimated, react-native-safe-area-context, react-native-worklets.

Requires the arrow-left, check and chevron-right icons in your icon registry. Positioning and the animation live in use-menu.ts. Colors come from menu.default.{default,pressed,destructive,disabled} in the component tokens.

Usage

import { Menu } from "@/components/ui/menu";

<Menu.Root>
	<Menu.Trigger asChild>
		<IconButton icon="more" accessibilityLabel="More options" />
	</Menu.Trigger>
	<Menu.Content align="end">
		<Menu.Item icon="share" onPress={share}>
			Share
		</Menu.Item>
		<Menu.Separator />
		<Menu.Item icon="trash" destructive onPress={remove}>
			Delete
		</Menu.Item>
	</Menu.Content>
</Menu.Root>;

Menu.Trigger opens the menu on a press. For a menu opened by a long press on content, with the content lifted above the backdrop, use ContextMenu: it takes the same parts.

Props

PropTypeDefaultDescription
openboolean—Controlled visibility.
defaultOpenbooleanfalseInitial visibility when the menu manages its own state.
onOpenChange(open: boolean) => void—Called when the menu opens or closes.
childrenReactNode—Trigger and Content.
PropTypeDefaultDescription
hapticHapticKind | false—Haptic played when the menu opens. Off unless you pass a kind.
asChildbooleanfalseUses the child as the trigger.
accessibilityLabelstring—Label of the trigger, when its content has no text.
childrenReactNode—The element that opens the menu.
styleStyleProp<ViewStyle>—Extra styles for the trigger.
PropTypeDefaultDescription
placement'auto' | 'top' | 'bottom''auto'Side of the trigger. auto picks the side with more room and keeps the menu inside the safe area.
align'start' | 'center' | 'end''start'Horizontal alignment against the trigger.
widthnumber250Menu width.
overlay'dim' | 'none''dim'Backdrop behind the menu. Pressing it closes the menu, even when none. A blur needs expo-blur: add it in your copy if you want one.
childrenReactNode—Items, groups and separators.
styleStyleProp<ViewStyle>—Extra styles for the menu surface.
PropTypeDefaultDescription
childrenstring—The label.
iconIconName—Icon on the right, iOS style.
subtitlestring—Second line under the label.
onPress() => void—Called after the menu closes, so a navigation or a sheet doesn’t fight the close animation.
destructivebooleanfalseLabel and icon in feedback.error.
disabledbooleanfalseDims the item and ignores presses.
checkedboolean—Shows a checkmark, for a menu that picks a sort order or a view.
keepOpenbooleanfalseRuns onPress right away without closing the menu, for a toggle inside the menu.
PartPropsDescription
GrouplabelGroups items under an optional small label.
Separator—A thick gap between groups, like iOS.
Sublabel, icon, childrenAn item that opens a nested menu in place.

Menu.Sub replaces the items in place and adds a back row at the top. Menu.Item also takes keepOpen, to run onPress without closing the menu. The menu has accessibilityRole="menu", its items menuitem, and a back action closes it.

Not implemented yet: dragging from the trigger to an item, and haptics (the haptics library depends on the project, see the roadmap).

Haptics

A menu opened by a press plays nothing by default. Pass a kind to haptic on Menu.Trigger if your app wants one. ContextMenu plays medium on a long press. Nothing plays until your app connects a haptics library: see Haptics.

Use cases

“More” button in an app bar

An IconButton as the trigger. The menu hangs from the button.

Q3 planning
Sort by

Date

Name

Rename

Delete

<Menu.Root>
	<Menu.Trigger asChild>
		<IconButton icon="more" accessibilityLabel="More options" />
	</Menu.Trigger>
	<Menu.Content align="end">
		<Menu.Group label="Sort by">
			<Menu.Item checked={sort === "date"} onPress={() => setSort("date")}>
				Date
			</Menu.Item>
			<Menu.Item checked={sort === "name"} onPress={() => setSort("name")}>
				Name
			</Menu.Item>
		</Menu.Group>
		<Menu.Separator />
		<Menu.Item icon="edit" onPress={rename}>
			Rename
		</Menu.Item>
		<Menu.Item icon="trash" destructive onPress={confirmDelete}>
			Delete
		</Menu.Item>
	</Menu.Content>
</Menu.Root>