Skip to main content

Menu

MD3 dropdown menu anchored to a trigger. It renders in the top layer, below the trigger (flipping above when there is no room) and clamped to the viewport, so ancestors' overflow or z-index never clip it.

Basic usage​

trigger is a single button-like element; the menu wires up its click, keyboard and ARIA attributes. The menu closes on item selection, outside click, Escape and Tab-out.

Last action: —
<Menu trigger={<Button variant="outlined">Open menu</Button>}>
<MenuItem startIcon={<EditIcon />} onClick={handleEdit}>Edit</MenuItem>
<MenuItem startIcon={<CopyIcon />}>Copy</MenuItem>
<MenuDivider />
<MenuItem disabled>Delete</MenuItem>
</Menu>

startIcon / endIcon are decorative. MenuDivider renders a separator.

Alignment and controlled state​

align="end" aligns the menu to the trigger's end edge — handy for an overflow button. Control the open state with open + onOpenChange, or set an initial state with defaultOpen.

Menu is closed
const [open, setOpen] = useState(false)

<Menu
align="end"
open={open}
onOpenChange={setOpen}
trigger={<IconButton icon={<MoreVertIcon />} aria-label="More options" />}
>
<MenuItem>Settings</MenuItem>
</Menu>

Vertical menu (Expressive)​

variant="vertical" is the MD3 Expressive vertical menu: segmented MenuGroup containers (with an optional section label), 44dp items, and a selected state. Setting selected (even to false) makes an item a menuitemcheckbox; a selected item morphs to a 12dp corner shape. color="vibrant" switches to the tertiary-based, higher-emphasis colors.

<Menu variant="vertical" color="vibrant" trigger={<Button>Format</Button>}>
<MenuGroup label="Format">
<MenuItem selected={bold} startIcon={<BoldIcon />} onClick={() => setBold(!bold)}>
Bold
</MenuItem>
</MenuGroup>
<MenuGroup>
<MenuItem startIcon={<CopyIcon />}>Copy</MenuItem>
</MenuGroup>
</Menu>

Accessibility​

Follows the WAI-ARIA APG menu-button pattern:

  • The trigger gets aria-haspopup / aria-expanded; the popup is role="menu" with menuitem (or menuitemcheckbox) items and role="group" for labelled MenuGroups.
  • Enter / Space / ↓ on the trigger open the menu and focus the first item (↑: the last). ↑ / ↓ cycle with wrap, Home / End jump, and typing a character moves focus by typeahead.
  • Escape and item activation close the menu and return focus to the trigger.
  • Items use a roving tabindex, so the menu never adds Tab stops to the page. Disabled items stay focusable but inert (aria-disabled).

Props​

PropTypeDefaultDescription
alignenumstartHorizontal alignment to the trigger.
children *ReactNode—`MenuItem`s — or, for the `vertical` variant, `MenuGroup`s.
colorenumstandardColor option for the `vertical` variant: `'standard'` (surface based) or `'vibrant'` (tertiary based, higher emphasis).
defaultOpenbooleanfalseUncontrolled initial open state.
onOpenChange((open: boolean) => void)—Notified when the open state should change.
openboolean—Controlled open state.
trigger *ReactElement<unknown, string | JSXElementConstructor<any>>—The element that opens the menu (a single button-like element).
variantenumstandard`'standard'` is the baseline dropdown menu; `'vertical'` is the MD3 Expressive vertical menu (segmented `MenuGroup` containers, 44dp items, selected states with shape morph).
PropTypeDefaultDescription
endIconReactNode—Icon at the end of the item (decorative).
selectedboolean—Selection state (for the menu's `vertical` variant). When set (even to `false`) the item becomes a `role="menuitemcheckbox"` with `aria-checked`; a selected item shows the tertiary-container mapping and morphs to a 12dp corner shape.
startIconReactNode—Icon at the start of the item (decorative).
PropTypeDefaultDescription
children *ReactNode—`MenuItem`s (and `MenuDivider`s).
labelReactNode—Optional section label rendered above the group's items.