Jone

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:

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

Control whether the menu closes on click
// 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.

Adding a submenu
<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>

Use the <Menu.LinkItem> part to create a link.

A menu item that opens 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.

Connecting a dialog to a menu
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.

Detached triggers
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.

Multiple triggers within the Root part
<Menu.Root>
  <Menu.Trigger>Row actions</Menu.Trigger>
  <Menu.Trigger>Quick actions</Menu.Trigger>
  {/* Rest of the menu */}
</Menu.Root>
Multiple detached triggers
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.

Detached triggers with 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

NameTypeDefaultDescription
defaultOpenOptionalboolean | undefinedfalseWhether the menu is initially open. To render a controlled menu, use the `open` prop instead.
loopFocusOptionalboolean | undefinedtrueWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys.
highlightItemOnHoverOptionalboolean | undefinedtrueWhether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.
modalOptionalboolean | undefinedtrueDetermines 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) | undefinedEvent handler called when the menu is opened or closed.
onOpenChangeCompleteOptional((open: boolean) => void) | undefinedEvent handler called after any animations complete when the menu is closed.
openOptionalboolean | undefinedWhether the menu is currently open.
orientationOptionalMenuRoot.Orientation | undefinedverticalThe visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys.
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.
closeParentOnEscOptionalboolean | undefinedfalseWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu.
actionsRefOptionalReact.RefObject<MenuRoot.Actions | null> | undefinedA 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.
triggerIdOptionalstring | null | undefinedID 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).
defaultTriggerIdOptionalstring | null | undefinedID of the trigger that the popover is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open popover.
handleOptionalMenuHandle<Payload> | undefinedA handle to associate the menu with a trigger. If specified, allows external triggers to control the menu's open state.
childrenOptionalReact.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

NameTypeDefaultDescription
childrenOptionalReact.ReactNode
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.
handleOptionalMenuHandle<Payload> | undefinedA handle to associate the trigger with a menu.
payloadOptionalPayload | undefinedA payload to pass to the menu when it is opened.
delayOptionalnumber | undefined100How long to wait before the menu may be opened on hover. Specified in milliseconds. Requires the `openOnHover` prop.
closeDelayOptionalnumber | undefined0How long to wait before closing the menu that was opened on hover. Specified in milliseconds. Requires the `openOnHover` prop.
openOnHoverOptionalboolean | undefinedWhether the menu should also open when the trigger is hovered.

Portal

NameTypeDefaultDescription
keepMountedOptionalboolean | undefinedfalseWhether to keep the portal mounted in the DOM while the popup is hidden.

Popup

NameTypeDefaultDescription
childrenOptionalReact.ReactNode
idOptionalstring | undefined@ignore

Item

NameTypeDefaultDescription
onClickOptionalBaseUIComponentProps<'div', MenuItemState>['onClick'] | undefinedThe click handler for the menu item.
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.
labelOptionalstring | undefinedOverrides the text label to use when the item is matched during keyboard text navigation.
idOptionalstring | undefined@ignore
closeOnClickOptionalboolean | undefinedtrueWhether to close the menu when the item is clicked.

LinkItem

NameTypeDefaultDescription
labelOptionalstring | undefinedOverrides the text label to use when the item is matched during keyboard text navigation.
idOptionalstring | undefined@ignore
closeOnClickOptionalboolean | undefinedfalseWhether to close the menu when the item is clicked.

SubmenuRoot

NameTypeDefaultDescription
closeParentOnEscOptionalboolean | undefinedfalseWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu.

SubmenuTrigger

NameTypeDefaultDescription
onClickOptionalBaseUIComponentProps<'div', MenuSubmenuTriggerState>['onClick'] | undefined
labelOptionalstring | undefinedOverrides the text label to use when the item is matched during keyboard text navigation.
idOptionalstring | undefined@ignore
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.
delayOptionalnumber | undefined100How long to wait before the menu may be opened on hover. Specified in milliseconds. Requires the `openOnHover` prop.
closeDelayOptionalnumber | undefined0How long to wait before closing the menu that was opened on hover. Specified in milliseconds. Requires the `openOnHover` prop.
openOnHoverOptionalboolean | undefinedWhether the menu should also open when the trigger is hovered.

Group

NameTypeDefaultDescription
childrenOptionalReact.ReactNodeThe content of the component.

RadioGroup

NameTypeDefaultDescription
childrenOptionalReact.ReactNodeThe content of the component.
valueOptionalanyThe controlled value of the radio item that should be currently selected. To render an uncontrolled radio group, use the `defaultValue` prop instead.
defaultValueOptionalanyThe uncontrolled value of the radio item that should be initially selected. To render a controlled radio group, use the `value` prop instead.
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.

RadioItem

NameTypeDefaultDescription
valueRequiredanyValue of the radio item. This is the value that will be set in the MenuRadioGroup when the item is selected.
onClickOptionalBaseUIComponentProps<'div', MenuRadioItemState>['onClick'] | undefinedThe click handler for the menu item.
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.
labelOptionalstring | undefinedOverrides the text label to use when the item is matched during keyboard text navigation.
idOptionalstring | undefined@ignore
closeOnClickOptionalboolean | undefinedfalseWhether to close the menu when the item is clicked.

RadioItemIndicator

NameTypeDefaultDescription
keepMountedOptionalboolean | undefinedfalseWhether to keep the HTML element in the DOM when the radio item is inactive.

CheckboxItem

NameTypeDefaultDescription
checkedOptionalboolean | undefinedWhether the checkbox item is currently ticked. To render an uncontrolled checkbox item, use the `defaultChecked` prop instead.
defaultCheckedOptionalboolean | undefinedfalseWhether the checkbox item is initially ticked. To render a controlled checkbox item, use the `checked` prop instead.
onClickOptionalBaseUIComponentProps<'div', MenuCheckboxItemState>['onClick'] | undefinedThe click handler for the menu item.
disabledOptionalboolean | undefinedfalseWhether the component should ignore user interaction.
labelOptionalstring | undefinedOverrides the text label to use when the item is matched during keyboard text navigation.
idOptionalstring | undefined@ignore
closeOnClickOptionalboolean | undefinedfalseWhether to close the menu when the item is clicked.

CheckboxItemIndicator

NameTypeDefaultDescription
keepMountedOptionalboolean | undefinedfalseWhether to keep the HTML element in the DOM when the checkbox item is not checked.

On this page