Menu
A high-quality, unstyled React menu component that displays list of actions in a dropdown, enhanced with keyboard navigation.
A list of actions in a dropdown, enhanced with keyboard navigation.
Anatomy
Import the component and assemble its parts:
import { Menu } from '@base-ui/react/menu';
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Backdrop />
<Menu.Positioner>
<Menu.Popup>
<Menu.Arrow />
<Menu.Item />
<Menu.LinkItem />
<Menu.Separator />
<Menu.Group>
<Menu.GroupLabel />
</Menu.Group>
<Menu.RadioGroup>
<Menu.RadioItem />
</Menu.RadioGroup>
<Menu.CheckboxItem />
<Menu.SubmenuRoot>
<Menu.SubmenuTrigger />
</Menu.SubmenuRoot>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>;Examples
Open on hover
To create a menu that opens on hover, add the openOnHover prop to <Menu.Trigger>. You can additionally configure how quickly the menu opens on hover using the delay prop.
Checkbox items
Use the <Menu.CheckboxItem> part to create a menu item that can toggle a setting on or off.
Radio items
Use the <Menu.RadioGroup> and <Menu.RadioItem> parts to create menu items that work like radio buttons.
Close on click
Use the closeOnClick prop to change whether the menu closes when an item is clicked.
// Close the menu when a checkbox item is clicked
<Menu.CheckboxItem closeOnClick />
// Keep the menu open when an item is clicked
<Menu.Item closeOnClick={false} />Group labels
Use the <Menu.GroupLabel> part to add a label to a <Menu.Group>
Nested menu
To create a submenu, nest another menu inside the parent menu with <Menu.SubmenuRoot>. Use the <Menu.SubmenuTrigger> part for the menu item that opens the nested menu.
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Arrow />
<Menu.Item />
{/* Submenu */}
<Menu.SubmenuRoot>
<Menu.SubmenuTrigger />
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
{/* prettier-ignore */}
{/* Submenu items */}
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.SubmenuRoot>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>Navigate to another page
Use the <Menu.LinkItem> part to create a link.
<Menu.LinkItem href="/projects">Go to Projects</Menu.LinkItem>Open a dialog
In order to open a dialog using a menu, control the dialog state and open it imperatively using the onClick handler on the menu item.
import * as React from 'react';
import { Dialog } from '@base-ui/react/dialog';
import { Menu } from '@base-ui/react/menu';
function ExampleMenu() {
const [dialogOpen, setDialogOpen] = React.useState(false);
return (
<React.Fragment>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
{/* Open the dialog when the menu item is clicked */}
<Menu.Item onClick={() => setDialogOpen(true)}>Open dialog</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
{/* Control the dialog state */}
<Dialog.Root open={dialogOpen} onOpenChange={setDialogOpen}>
<Dialog.Portal>
<Dialog.Backdrop />
<Dialog.Popup>
{/* prettier-ignore */}
{/* Rest of the dialog */}
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
</React.Fragment>
);
}Detached triggers
A menu can be opened by a trigger that lives either inside or outside the <Menu.Root>.
Keep the trigger inside <Menu.Root> for simple, tightly coupled layouts like the hero demo at the top of this page.
When the trigger and menu content need to live in different parts of the tree (for example, in a card list that controls a menu rendered near the document root), create a handle with Menu.createHandle() and pass it to both the trigger and the root.
Note that only top-level menus can have detached triggers.
Submenus must have their triggers defined within the SubmenuRoot part.
const demoMenu = Menu.createHandle();
<Menu.Trigger handle={demoMenu}>
Actions
</Menu.Trigger>
<Menu.Root handle={demoMenu}>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item>Edit</Menu.Item>
<Menu.Item>Share</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>Multiple triggers
One menu can be opened by several triggers.
You can either render multiple <Menu.Trigger> components inside the same <Menu.Root>, or attach several detached triggers to the same handle.
<Menu.Root>
<Menu.Trigger>Row actions</Menu.Trigger>
<Menu.Trigger>Quick actions</Menu.Trigger>
{/* Rest of the menu */}
</Menu.Root>const projectMenu = Menu.createHandle();
<Menu.Trigger handle={projectMenu}>Row actions</Menu.Trigger>
<Menu.Trigger handle={projectMenu}>Quick actions</Menu.Trigger>
<Menu.Root handle={projectMenu}>
{/* Rest of the menu */}
</Menu.Root>Menus can render different content depending on which trigger opened them.
Pass a payload prop to each <Menu.Trigger> and read it via a function child on <Menu.Root>.
Provide a type argument to createHandle() to strongly type the payload.
const menus = {
file: ['New', 'Open', 'Save'],
edit: ['Undo', 'Redo', 'Cut', 'Copy', 'Paste'],
}
const demoMenu = Menu.createHandle<{ items: string[] }>();
<Menu.Trigger handle={demoMenu} payload={{ items: menus.file }}>
File
</Menu.Trigger>
<Menu.Trigger handle={demoMenu} payload={{ items: menus.edit }}>
Edit
</Menu.Trigger>
<Menu.Root handle={demoMenu}>
{({ payload }) => (
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
{(payload?.items ?? []).map((item) => (
<Menu.Item key={item}>{item}</Menu.Item>
))}
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
)}
</Menu.Root>Controlled mode with multiple triggers
Control a menu's open state externally with the open and onOpenChange props on <Menu.Root>.
When more than one trigger can open the menu, track the active trigger with the triggerId prop on <Menu.Root> and matching id props on each <Menu.Trigger>.
The onOpenChange callback receives eventDetails, which includes the DOM element that initiated the change, so you can update your triggerId state when the user activates a different trigger.
API reference
Root
| Name | Type | Default | Description |
|---|---|---|---|
defaultOpenOptional | boolean | undefined | false | Whether the menu is initially open. To render a controlled menu, use the `open` prop instead. |
loopFocusOptional | boolean | undefined | true | Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. |
highlightItemOnHoverOptional | boolean | undefined | true | Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. |
modalOptional | boolean | undefined | true | Determines if the menu enters a modal state when open. - `true`: user interaction is limited to the menu: document page scroll is locked and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. |
onOpenChangeOptional | ((open: boolean, eventDetails: MenuRoot.ChangeEventDetails) => void) | undefined | — | Event handler called when the menu is opened or closed. |
onOpenChangeCompleteOptional | ((open: boolean) => void) | undefined | — | Event handler called after any animations complete when the menu is closed. |
openOptional | boolean | undefined | — | Whether the menu is currently open. |
orientationOptional | MenuRoot.Orientation | undefined | vertical | The visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
closeParentOnEscOptional | boolean | undefined | false | When in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu. |
actionsRefOptional | React.RefObject<MenuRoot.Actions | null> | undefined | — | A ref to imperative actions. - `unmount`: When specified, the menu will not be unmounted when closed. Instead, the `unmount` function must be called to unmount the menu manually. Useful when the menu's animation is controlled by an external library. - `close`: When specified, the menu can be closed imperatively. |
triggerIdOptional | string | null | undefined | — | ID of the trigger that the popover is associated with. This is useful in conjunction with the `open` prop to create a controlled popover. There's no need to specify this prop when the popover is uncontrolled (i.e. when the `open` prop is not set). |
defaultTriggerIdOptional | string | null | undefined | — | ID of the trigger that the popover is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open popover. |
handleOptional | MenuHandle<Payload> | undefined | — | A handle to associate the menu with a trigger. If specified, allows external triggers to control the menu's open state. |
childrenOptional | React.ReactNode | PayloadChildRenderFunction<Payload> | — | The content of the popover. This can be a regular React node or a render function that receives the `payload` of the active trigger. |
Trigger
| Name | Type | Default | Description |
|---|---|---|---|
childrenOptional | React.ReactNode | — | — |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
handleOptional | MenuHandle<Payload> | undefined | — | A handle to associate the trigger with a menu. |
payloadOptional | Payload | undefined | — | A payload to pass to the menu when it is opened. |
delayOptional | number | undefined | 100 | How long to wait before the menu may be opened on hover. Specified in milliseconds. Requires the `openOnHover` prop. |
closeDelayOptional | number | undefined | 0 | How long to wait before closing the menu that was opened on hover. Specified in milliseconds. Requires the `openOnHover` prop. |
openOnHoverOptional | boolean | undefined | — | Whether the menu should also open when the trigger is hovered. |
Portal
| Name | Type | Default | Description |
|---|---|---|---|
keepMountedOptional | boolean | undefined | false | Whether to keep the portal mounted in the DOM while the popup is hidden. |
Popup
| Name | Type | Default | Description |
|---|---|---|---|
childrenOptional | React.ReactNode | — | — |
idOptional | string | undefined | — | @ignore |
Item
| Name | Type | Default | Description |
|---|---|---|---|
onClickOptional | BaseUIComponentProps<'div', MenuItemState>['onClick'] | undefined | — | The click handler for the menu item. |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
labelOptional | string | undefined | — | Overrides the text label to use when the item is matched during keyboard text navigation. |
idOptional | string | undefined | — | @ignore |
closeOnClickOptional | boolean | undefined | true | Whether to close the menu when the item is clicked. |
LinkItem
| Name | Type | Default | Description |
|---|---|---|---|
labelOptional | string | undefined | — | Overrides the text label to use when the item is matched during keyboard text navigation. |
idOptional | string | undefined | — | @ignore |
closeOnClickOptional | boolean | undefined | false | Whether to close the menu when the item is clicked. |
SubmenuRoot
| Name | Type | Default | Description |
|---|---|---|---|
closeParentOnEscOptional | boolean | undefined | false | When in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu. |
SubmenuTrigger
| Name | Type | Default | Description |
|---|---|---|---|
onClickOptional | BaseUIComponentProps<'div', MenuSubmenuTriggerState>['onClick'] | undefined | — | — |
labelOptional | string | undefined | — | Overrides the text label to use when the item is matched during keyboard text navigation. |
idOptional | string | undefined | — | @ignore |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
delayOptional | number | undefined | 100 | How long to wait before the menu may be opened on hover. Specified in milliseconds. Requires the `openOnHover` prop. |
closeDelayOptional | number | undefined | 0 | How long to wait before closing the menu that was opened on hover. Specified in milliseconds. Requires the `openOnHover` prop. |
openOnHoverOptional | boolean | undefined | — | Whether the menu should also open when the trigger is hovered. |
Group
| Name | Type | Default | Description |
|---|---|---|---|
childrenOptional | React.ReactNode | — | The content of the component. |
RadioGroup
| Name | Type | Default | Description |
|---|---|---|---|
childrenOptional | React.ReactNode | — | The content of the component. |
valueOptional | any | — | The controlled value of the radio item that should be currently selected. To render an uncontrolled radio group, use the `defaultValue` prop instead. |
defaultValueOptional | any | — | The uncontrolled value of the radio item that should be initially selected. To render a controlled radio group, use the `value` prop instead. |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
RadioItem
| Name | Type | Default | Description |
|---|---|---|---|
valueRequired | any | — | Value of the radio item. This is the value that will be set in the MenuRadioGroup when the item is selected. |
onClickOptional | BaseUIComponentProps<'div', MenuRadioItemState>['onClick'] | undefined | — | The click handler for the menu item. |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
labelOptional | string | undefined | — | Overrides the text label to use when the item is matched during keyboard text navigation. |
idOptional | string | undefined | — | @ignore |
closeOnClickOptional | boolean | undefined | false | Whether to close the menu when the item is clicked. |
RadioItemIndicator
| Name | Type | Default | Description |
|---|---|---|---|
keepMountedOptional | boolean | undefined | false | Whether to keep the HTML element in the DOM when the radio item is inactive. |
CheckboxItem
| Name | Type | Default | Description |
|---|---|---|---|
checkedOptional | boolean | undefined | — | Whether the checkbox item is currently ticked. To render an uncontrolled checkbox item, use the `defaultChecked` prop instead. |
defaultCheckedOptional | boolean | undefined | false | Whether the checkbox item is initially ticked. To render a controlled checkbox item, use the `checked` prop instead. |
onClickOptional | BaseUIComponentProps<'div', MenuCheckboxItemState>['onClick'] | undefined | — | The click handler for the menu item. |
disabledOptional | boolean | undefined | false | Whether the component should ignore user interaction. |
labelOptional | string | undefined | — | Overrides the text label to use when the item is matched during keyboard text navigation. |
idOptional | string | undefined | — | @ignore |
closeOnClickOptional | boolean | undefined | false | Whether to close the menu when the item is clicked. |
CheckboxItemIndicator
| Name | Type | Default | Description |
|---|---|---|---|
keepMountedOptional | boolean | undefined | false | Whether to keep the HTML element in the DOM when the checkbox item is not checked. |