Menu
A button that opens a list of actions. Menu.Trigger is a Button.Root with aria-haspopup="menu" and aria-expanded. Menu.Content is a role="menu" in a non-modal popover anchored to the trigger with CSS anchor positioning, the same surface as Popover. The menu is named by its trigger unless it gets aria-label or aria-labelledby.
<Menu.Root>
<Menu.Trigger variant="secondary">Actions</Menu.Trigger>
<Menu.Content>
<Menu.Item onSelect={rename}>Rename</Menu.Item>
<Menu.Separator />
<Menu.Item onSelect={remove}>Delete</Menu.Item>
</Menu.Content>
</Menu.Root>
Last action: nothing yet
Keyboard
Enter, Space, a click or ArrowDown on the trigger opens the menu on its first item; ArrowUp opens it on the last. In a vertical toolbar, Up and Down move between the toolbar's controls instead. In the menu, Up and Down move and loop, Home and End jump, and typing a letter moves to the next item that starts with it. Enter, Space or a click chooses an item. Escape closes and returns focus to the trigger; Tab closes and moves focus past the trigger (in a toolbar, past the trigger's run of controls); Shift+Tab closes and stops on the trigger. Focus that moves anywhere else outside the menu, by a script, a screen reader or a press into an iframe, closes it too. Pointer movement focuses the item under it, so the pointer and the keyboard share one highlight. Disabled items carry aria-disabled and stay in the arrow-key order, as menus conventionally do, so a screen reader announces them as unavailable; they cannot be chosen. Hovering one focuses it, as the arrow keys do, so the highlight never stays on another item.
Checkbox and radio items
An item's children are presentational to assistive technology, as ARIA defines for menuitem, so keep links, inputs and buttons out of items; keys pressed in one are left alone, but a click on it also chooses the item. Menu.CheckboxItem toggles aria-checked. Menu.RadioGroup owns one value for its Menu.RadioItem children. Both stay open after a choice so several settings can change in one visit; set closeOnSelect to close. Menu.Group and Menu.RadioGroup need aria-label or aria-labelledby.
onOpenChange(open, reason) reports trigger, escape, outside (a press or focus outside), select or tab. A controlled open that is not updated vetoes the change; a vetoed tab still lets focus move on, the way a vetoed outside still lets the press land. Submenus are not shipped.
When to use
When: A short list of commands or view settings behind one button, such as row actions or a toolbar overflow.
When not: Picking a value for a form → Field.Select or the Combobox recipe. Navigation between pages → links. Content with its own controls or text → Popover with role="dialog".
Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| open | boolean | no | — | Controlled open state. |
| defaultOpen | boolean | no | — | Initial open state when uncontrolled. |
| onOpenChange | (open: boolean, reason: MenuOpenChangeReason) => void | no | — | Fires on open and close. reason is trigger, escape, outside, select or tab. Leaving open unchanged is a veto. |
| children | React.ReactNode | no | — | Menu.Trigger and Menu.Content. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| variant | ButtonVariant | no | "primary" | Button variant. |
| tooltip | boolean | no | false | Button tooltip. Off by default on a menu trigger. |
| children | React.ReactNode | no | — | Trigger label. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| aria-label | string | no | — | Names the menu instead of the trigger. |
| aria-labelledby | string | no | — | Id of the element that names the menu. Default is the trigger. |
| children | React.ReactNode | no | — | Items, groups and separators. |
| className | string | no | — | Optional class. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| onSelect | () => void | no | — | Runs when the item is chosen. |
| closeOnSelect | boolean | no | true | Close the menu after choosing. |
| disabled | boolean | no | — | aria-disabled. Stays in the arrow-key order, so it is announced, but cannot be chosen. |
| textValue | string | no | — | Typeahead text when children are not plain text. |
| children | React.ReactNode | no | — | Item label. |
| className | string | no | — | Optional class. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| checked | boolean | no | — | Controlled checked state. |
| defaultChecked | boolean | no | — | Initial checked state when uncontrolled. |
| onCheckedChange | (checked: boolean) => void | no | — | Fires with the next checked state. |
| closeOnSelect | boolean | no | false | Close the menu after toggling. |
| disabled | boolean | no | — | aria-disabled. Stays in the arrow-key order, so it is announced, but cannot be chosen. |
| textValue | string | no | — | Typeahead text when children are not plain text. |
| children | React.ReactNode | no | — | Item label. |
| className | string | no | — | Optional class. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| aria-label | string | no | — | Group name. Exactly one of aria-label or aria-labelledby. |
| aria-labelledby | string | no | — | Id of a visible group caption. |
| children | React.ReactNode | no | — | Items. |
| className | string | no | — | Optional class. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| value | string | null | no | — | Controlled value. null is controlled with nothing checked. |
| defaultValue | string | null | no | — | Initial value when uncontrolled. |
| onValueChange | (value: string) => void | no | — | Fires with the next value. |
| aria-label | string | no | — | Group name. Exactly one of aria-label or aria-labelledby. |
| aria-labelledby | string | no | — | Id of a visible group caption. |
| children | React.ReactNode | no | — | Menu.RadioItem children. |
| className | string | no | — | Optional class. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| value | string | yes | — | Value this item sets on its group. |
| closeOnSelect | boolean | no | false | Close the menu after choosing. |
| disabled | boolean | no | — | aria-disabled. Stays in the arrow-key order, so it is announced, but cannot be chosen. |
| textValue | string | no | — | Typeahead text when children are not plain text. |
| children | React.ReactNode | no | — | Item label. |
| className | string | no | — | Optional class. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| className | string | no | — | Optional class. |