A menu of actions that opens from a trigger button, with icons, shortcut hints, dividers ({ key, divider: true }) and tone="danger" items. Arrow keys move, Home / End jump, Enter or Space chooses, and Escape closes the menu and returns focus to the trigger. Outside clicks close it. Items take the same fields as ActionMenu: description, badge, href (a real link, with linkComponent / linkProp for router links), tone="warning" and hasDialog. For a plain, flat list of page actions, ActionMenu is lighter.
Last action: nothing yet
Nested menus: give an item children to make it open a submenu, to any depth. Hovering the item or pressing Right opens it, Left or Escape closes just that level, and a click anywhere in an open submenu counts as inside the menu.
Last action: nothing yet. Try the arrow keys: Right opens a submenu, Left closes it.
Leave out trigger and pass anchorRef with open / onOpenChange to anchor the menu to your own element and control it yourself.
Controlled with open and anchorRef. Last action: nothing yet
Same look as ActionMenu: look="actions" gives the menu ActionMenu's appearance (uppercase labels, ruled rows, a fixed width), also in submenus. Use it when the app shows both menus, or when an action menu needs a submenu.
Headings: an item with a heading is a group label, not an item. The items after it, up to the next heading or divider, are grouped under it for screen readers (role="group"), and arrow keys skip it. It works in both looks. Or nest them: an item with items is a group, its label is the heading, and the items go inside it (dividers and submenus work there too).
Props
Menu
itemsMenuItem[]The entries.
childrenmakes a submenu;{ key, divider: true }is a separator. See the MenuItem table below.triggerReactNodeContent of the trigger button; leave it out to use
anchorRef.labelstringAccessible name of the trigger and the menu.
variant'default' | 'secondary' | 'flat' | 'success' | 'danger' | 'warning'Default:'secondary'Style of the trigger button.
placement'bottom-start' | 'bottom-end' | 'top-start' | ...Default:'bottom-start'Where the menu opens; it flips when there is no room.
look'default' | 'actions'Default:'default'The look of the menu: "actions" matches ActionMenu (uppercase labels, ruled rows, a fixed width); the default is the compact one.
linkComponentElementTypeDefault:'a'Component used for items with a site path as href (not absolute URLs), e.g. a router Link.
linkPropstringDefault:'href'Prop that receives the href on linkComponent, e.g. "to".
openbooleanOpen state (controlled).
defaultOpenbooleanDefault:falseInitial open state when uncontrolled.
onOpenChange(open: boolean) => voidCalled when it opens or closes.
anchorRefRefObject<HTMLElement>Element the menu is placed next to when there is no
trigger.classNamestringDefault:''Extra classes for the root element.
| Prop | Type | Default | Description |
|---|---|---|---|
items | MenuItem[] | – | The entries. children makes a submenu; { key, divider: true } is a separator. See the MenuItem table below. |
trigger | ReactNode | – | Content of the trigger button; leave it out to use anchorRef. |
label | string | – | Accessible name of the trigger and the menu. |
variant | 'default' | 'secondary' | 'flat' | 'success' | 'danger' | 'warning' | 'secondary' | Style of the trigger button. |
placement | 'bottom-start' | 'bottom-end' | 'top-start' | ... | 'bottom-start' | Where the menu opens; it flips when there is no room. |
look | 'default' | 'actions' | 'default' | The look of the menu: "actions" matches ActionMenu (uppercase labels, ruled rows, a fixed width); the default is the compact one. |
linkComponent | ElementType | 'a' | Component used for items with a site path as href (not absolute URLs), e.g. a router Link. |
linkProp | string | 'href' | Prop that receives the href on linkComponent, e.g. "to". |
open | boolean | – | Open state (controlled). |
defaultOpen | boolean | false | Initial open state when uncontrolled. |
onOpenChange | (open: boolean) => void | – | Called when it opens or closes. |
anchorRef | RefObject<HTMLElement> | – | Element the menu is placed next to when there is no trigger. |
className | string | '' | Extra classes for the root element. |
MenuItem
keystringUnique key of the item.
labelReactNodeText of the item (not needed for a divider or heading).
headingReactNodeA group label instead of an item; the items after it, up to the next heading or divider, are grouped under it.
itemsMenuItem[]Makes the item a group: its label is the heading and these are the items under it (dividers and submenus work inside).
iconReactNodeShown before the label, hidden from screen readers.
descriptionReactNodeA smaller grey line under the label.
badgeReactNodeShort value at the end of the row, e.g. a count.
shortcutstringA keyboard shortcut hint at the end, e.g. "Ctrl+C" (display only).
disabledbooleanDefault:falseDim the item and ignore it.
tone'default' | 'warning' | 'danger'Default:'default'Color of the item; "danger" is red, for destructive actions.
hasDialogbooleanDefault:falseMarks an item that opens a dialog (aria-haspopup="dialog").
hrefstringMakes the item a real link instead of a button; onClick is then optional.
targetstringFor link items: where to open it. "_blank" opens a new tab and adds rel="noopener noreferrer".
externalbooleanShow the external-link arrow. By default a link is external when it has target="_blank" or an absolute http(s) address.
onClick() => voidCalled when the item is chosen.
childrenMenuItem[]Makes the item open a submenu with these items.
dividerbooleanDefault:falseA separator line instead of an item (only key is needed).
| Prop | Type | Default | Description |
|---|---|---|---|
key | string | – | Unique key of the item. |
label | ReactNode | – | Text of the item (not needed for a divider or heading). |
heading | ReactNode | – | A group label instead of an item; the items after it, up to the next heading or divider, are grouped under it. |
items | MenuItem[] | – | Makes the item a group: its label is the heading and these are the items under it (dividers and submenus work inside). |
icon | ReactNode | – | Shown before the label, hidden from screen readers. |
description | ReactNode | – | A smaller grey line under the label. |
badge | ReactNode | – | Short value at the end of the row, e.g. a count. |
shortcut | string | – | A keyboard shortcut hint at the end, e.g. "Ctrl+C" (display only). |
disabled | boolean | false | Dim the item and ignore it. |
tone | 'default' | 'warning' | 'danger' | 'default' | Color of the item; "danger" is red, for destructive actions. |
hasDialog | boolean | false | Marks an item that opens a dialog (aria-haspopup="dialog"). |
href | string | – | Makes the item a real link instead of a button; onClick is then optional. |
target | string | – | For link items: where to open it. "_blank" opens a new tab and adds rel="noopener noreferrer". |
external | boolean | – | Show the external-link arrow. By default a link is external when it has target="_blank" or an absolute http(s) address. |
onClick | () => void | – | Called when the item is chosen. |
children | MenuItem[] | – | Makes the item open a submenu with these items. |
divider | boolean | false | A separator line instead of an item (only key is needed). |