Components

Menu

A React menu with a Liquid Glass popup, groups, separators, icons, shortcuts, and controlled open state.

Menu renders the trigger, portal, positioner, glass popup, list, action items, groups, and separators internally. Selecting an item closes the menu by default and calls its onSelect callback.

Items are passed through the items array. Use type: "group" for a labelled section and type: "separator" for a divider. Action items need a unique value for React keys. When the visible label is not plain text, pass textValue so keyboard typeahead can match it.

Basic example

The example combines grouped actions, a separator, icons, shortcuts, and a disabled item. The status text below the trigger uses aria-live="polite" so screen readers hear the selected action.

No action selected.

Placement

side, align, sideOffset, and alignOffset control the popup position. The menu flips or shifts when it would overflow the viewport.

Controlled usage

Pass open and onOpenChange to own visibility. The trigger still toggles the menu unless you also control it externally.

The menu is closed.

Accessibility

Menu follows the menu-button pattern: the trigger exposes aria-haspopup="menu", aria-expanded, and aria-controls; the inner list carries role="menu", and each action is a menuitem. ArrowDown and ArrowUp move through enabled items, and typeahead matches item labels. Enter or Space activates the highlighted item, and Escape closes the menu and returns focus to the trigger.

Disabled items stay announced but cannot be activated. value is only the React key. When label is not plain text, pass textValue so typeahead has a readable label, and pass aria-label or aria-labelledby when the item also needs an accessible name. See the accessibility guide for the shared interaction patterns.

API reference

Menu

PropTypeDefaultDescription
triggerReactElement—Element that opens the menu. Required. It must forward props and refs.
itemsreadonly (MenuItemProps | MenuGroupProps | MenuSeparatorProps)[]—Action items, groups, and separators rendered by the menu.
side"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"Preferred side of the trigger.
align"start" | "center" | "end""start"Alignment relative to the trigger.
sideOffsetnumber | OffsetFunction8Distance between the trigger and menu in pixels.
alignOffsetnumber | OffsetFunction0Offset along the alignment axis in pixels.
collisionPaddingPadding5px per sideSpace kept between the popup and the collision boundary.
openboolean—Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean, details) => void—Called when the menu opens or closes.
onOpenChangeComplete(open: boolean) => void—Called after the open or close animation finishes.
modalbooleantrueLocks outside interaction while the menu is open.
loopFocusbooleantrueLoops keyboard focus through the items.
highlightItemOnHoverbooleantrueHighlights items when the pointer moves over them.
orientation"horizontal" | "vertical""vertical"Roving focus axis used by the arrow keys.
closeParentOnEscbooleanfalseIn a nested menu, closes the whole menu on Escape instead of only the child menu.
onItemHighlighted(item: HTMLElement | undefined, details) => void—Called when the highlighted item changes.
actionsRefRefObject<Menu.Root.Actions | null>—Imperative close and unmount actions.
triggerIdstring | null—ID of the trigger for a controlled menu.
defaultTriggerIdstring | null—ID of the trigger for an initially open menu.
handleMenu.Handle<Payload>—Handle that lets external triggers control the menu.
disabledbooleanfalsePrevents the menu from opening.
classNamestring—Tailwind classes merged into the popup.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ positioner?: ClassValue; popup?: ClassValue; glass?: ClassValue; overlay?: ClassValue; list?: ClassValue; item?: ClassValue; itemIcon?: ClassValue; itemLabel?: ClassValue; shortcut?: ClassValue; group?: ClassValue; groupLabel?: ClassValue; separator?: ClassValue }—Tailwind classes for internal slots, keyed by data-slot.

MenuItemProps

PropTypeDefaultDescription
type"item"—Marks the entry as an action item. Optional when the entry has no group or separator type.
valuestring—Unique value used as the React key for this item. Required.
labelReactNode—Visible content inside the item. Required.
textValuestring—Plain-text label used for keyboard typeahead. Defaults to a string label.
iconReactNode—Decorative icon rendered before the label.
shortcutReactNode—Shortcut hint rendered after the label. It is visual only.
onSelect(event) => void—Called when the item is selected.
disabledbooleanfalseDisables the item.
closeOnClickbooleantrueWhether selecting the item closes the menu.
idstring—ID of the item element.
nativeButtonbooleanfalseWhether the rendered element is a native <button>.
renderReactElement | ((props, state) => ReactElement)—Replaces the item element.
classNamestring—Tailwind classes merged into the item.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ item?: ClassValue; itemIcon?: ClassValue; itemLabel?: ClassValue; shortcut?: ClassValue }—Tailwind classes for this item's slots.

MenuGroupProps

PropTypeDefaultDescription
type"group"—Marks the entry as a group. Required.
labelReactNode—Group heading. Required.
itemsreadonly MenuItemProps[]—Items rendered inside the group. Required.
renderReactElement | ((props, state) => ReactElement)—Replaces the group element.
classNamestring—Tailwind classes merged into the group.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ group?: ClassValue; groupLabel?: ClassValue; item?: ClassValue; itemIcon?: ClassValue; itemLabel?: ClassValue; shortcut?: ClassValue }—Tailwind classes for the group and its item slots.

MenuGroupProps also forwards the remaining Menu.Group props.

MenuSeparatorProps

PropTypeDefaultDescription
type"separator"—Marks the entry as a separator. Required.
renderReactElement | ((props, state) => ReactElement)—Replaces the separator element.
classNamestring—Tailwind classes merged into the separator.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ separator?: ClassValue }—Tailwind classes for the separator slot.

MenuSeparatorProps also forwards Menu.Separator props such as render and id.

ui keys map to the data-slot values listed below. All other Base UI Menu.Root props are forwarded.

AttributeDescription
data-openPresent on the popup when the menu is open.
data-closedPresent on the popup while it is closed or closing.
data-highlightedPresent on the keyboard- or pointer-highlighted item.
data-disabledPresent when the menu or an item is disabled.
data-popup-openPresent on the trigger while the menu is open.
data-sideIndicates the resolved side of the popup.
data-alignIndicates the resolved alignment (start, center, or end).
data-instantPresent when animations should be skipped.
data-starting-stylePresent when the popup begins animating in.
data-ending-stylePresent when the popup is animating out.
data-slotMarks internal parts (positioner, popup, glass, overlay, list, item, item-icon, item-label, shortcut, group, group-label, separator).

Theming

The component uses the shared glass material. Tune shared --ui-glass-* tokens on a parent, use className for layout, and use ui for internal slots keyed by data-slot. Component-specific values live in src/theme/menu.ts. See the theming guide for token layers and presets.

  • Button — The usual menu trigger and the action style used inside items.
  • Popover — For richer content that needs a title, body, and actions.
  • Select — For choosing one option from a list instead of running an action.