Menu

Menus display a list of choices on a temporary surface.

One family, one documentation

Menu, MenuItem, MenuGroup, MenuHeadline and ContextMenu form a single family. This page is their shared documentation; each component keeps a dedicated API reference:

Both adapters use the same core contract, the same styles and the same keyboard navigation engine.

Choosing the right semantics

Use purpose="actions" (the default) for commands. The container then exposes menu and its items expose menuitem, menuitemradio or menuitemcheckbox.

Use purpose="selection" for a list of options. The container becomes a listbox, the items become options and their state is announced with aria-selected.

accessibleLabel names the menu when no external label is linked to it.

Composition

  • MenuItem represents a command or a choice.
  • MenuGroup groups items and its label names the group.
  • MenuHeadline is a non-interactive visual marker. It does not replace the semantic label of a MenuGroup. In a menu made of groups, place it inside the relevant MenuGroup so it stays within its surface.
  • ContextMenu opens an actions menu at the pointer position or via Shift+F10.

Variants, icons and states

variant="standard" uses the neutral surface. variant="vibrant" applies the family color to the whole composition. Leading and trailing icons remain optional; a disabled item keeps its structure but receives no interaction or state layer.

When there is no MenuGroup, a MenuHeadline can be placed directly inside Menu.

Controlled and uncontrolled selection

A selectable item declares selectionType="single" or selectionType="multiple".

  • In React, control it with selected and onSelectedChange.
  • In Angular, use selected and selectedChange, including with [(selected)].
  • In Svelte, use bind:selected or selected with onSelectedChange.
  • defaultSelected initializes an uncontrolled item.
  • An already selected single item does not deselect itself; the group’s owner picks the new value.

The former names activated and onToggle were removed: they mixed resolved state with the event, and did not distinguish single from multiple selection.

The following example brings both models together:

  • an actions menu with radio choices (single) and checkboxes (multiple);
  • a list of options with purpose="selection";
  • controlled states and one state initialized with defaultSelected.

With href, a MenuItem becomes a native link. A disabled link loses its destination and leaves the tab order. When the content exceeds the menu’s maximum height, the surface becomes scrollable without changing keyboard navigation.

TextField with type="select" opens a purpose="selection" surface, controls the selected value and closes the menu after the choice. Both adapters share this behavior; only the way options are declared differs:

  • in React, compose MenuItem children directly, or pass options;
  • in Angular, options is the only way to populate the menu: the adapter does not project custom MenuItems.
  • in Svelte, options is also the select-field model; custom MenuItem children are not projected by this mode.

Context menu

The context menu opens on secondary click or with Shift+F10. It moves focus to the first available item. Escape closes the popup and restores focus to the trigger; an outside click or a scroll also closes it.

Keyboard navigation

In an open menu:

  • Arrow down/up moves focus and wraps at both ends.
  • Home/End goes to the first/last available item.
  • Typing a character searches for the next matching label.
  • Disabled items are skipped.