ContextMenu
One set of actions, two ways to open
Supply readable labels and stable unique item IDs. The context surface and visible Actions button use the same items, disabled states and onAction callback.
Right-click the record ID, focus it and use Shift+F10 or the context-menu key, or choose Actions. Archive is unavailable. Shortcut text describes an application binding; this demo does not install it.
<ContextMenu
label="Alpha relay"
items={[
{ id: "inspect", label: "Inspect", shortcut: "Ctrl+I" },
{ id: "archive", label: "Archive", disabled: true },
{ id: "delete", label: "Delete", danger: true },
]}
onAction={handleResourceAction}
>
<span>EQ-01</span>
</ContextMenu>
The surface adds a focus indicator around your supplied content without imposing a background or padded container. Native inputs and buttons inside it keep their normal typing and activation. Context activation applies to the resource surface; avoid wrapping controls that require a separate native context menu.
Keyboard and pointer access
Right-click the surface, or focus it and use Shift+F10, the context-menu key, or Ctrl+Enter on macOS. On Android a long press opens the menu too. On iPhone and iPad no browser reports a long press as a context-menu request (they all use WebKit), so the visible button is the touch path there; an iPad with a pointer still opens the menu with a secondary click. It is named “Alpha relay actions” in this example, looks like a small secondary Button and opens with Enter or Space. Arrow keys navigate menu items. Escape closes the menu and returns focus to its activation surface or button.
shortcut is display text describing an application-owned key binding; it describes the item to assistive technology. ContextMenu does not install global shortcuts. danger adds a visible “Destructive action” description as well as color; your application still owns confirmation and the action's outcome. DropdownMenu shows actions the same way.
Fitting the screen
Menu labels and identifiers wrap and the rows grow. The menu is about 30rem wide at most and never wider than the screen. When the actions are taller than the space beside the trigger, the menu scrolls inside itself instead of covering the trigger.
No actions
With an empty items list, the menu shows a non-interactive “No actions available” row. emptyMessage changes its text.
Disabled actions
An item's disabled flag prevents its activation in both menus. The component's disabled prop closes an open menu and disables both ways to open it. Supplied child controls retain their own disabled policy.
Props
| PROP | TYPE | DEFAULT | REQUIRED |
|---|---|---|---|
labelNames the surface; the visible button is named `${label} actions`. | string | — | yes |
itemsStable unique id and readable label, with optional shortcut text, disabled and danger flags. | array | — | yes |
onActionReceives the selected item ID. Both activation paths use the same supplied actions. | function | — | yes |
disabledDisables both menu activation paths without disabling supplied child content. | boolean | false | no |
emptyMessageNon-interactive row the menu shows when items is empty. | string | "No actions available" | no |
className | string | — | no |
The ref targets the outer div; native div attributes pass through. Both menu paths use installed React Aria interactions, item navigation, positioning and focus management. There are no submenu APIs in this component.
Accessibility
- The focusable surface is a named group; its child controls preserve their semantics.
- A visible native menu button provides the same actions as context activation.
- Menus are named, disabled items are announced and dangerous actions have a written description.
- Interactive labels use the base text token and targets are at least 24px high.
- Token focus indicators remain visible on the surface, button and menu items.
- Menu labels wrap, and the menu fits the viewport, scrolling inside itself when it is taller than the space beside its trigger.
Manifest entry
<ContextMenu label="Alpha relay" items={[{id: "inspect", label: "Inspect"}]} onAction={handleAction}>EQ-01</ContextMenu>View agent manifest
{
"description": "Context actions with a focusable surface and an equivalent visible menu button.",
"props": {
"label": {
"type": "string",
"required": true,
"description": "Names the surface; the visible button is named `${label} actions`."
},
"items": {
"type": "array",
"required": true,
"description": "Stable unique id and readable label, with optional shortcut text, disabled and danger flags."
},
"onAction": {
"type": "function",
"required": true,
"description": "Receives the selected item ID. Both activation paths use the same supplied actions."
},
"disabled": {
"type": "boolean",
"default": false,
"description": "Disables both menu activation paths without disabling supplied child content."
},
"emptyMessage": {
"type": "string",
"default": "No actions available",
"description": "Non-interactive row the menu shows when items is empty."
},
"className": {
"type": "string"
}
},
"slots": {
"children": {
"type": "ReactNode",
"required": true
}
},
"composable_with": [
"InlineEdit",
"DataGrid"
],
"never_inside": [
"Button"
],
"example": "<ContextMenu label=\"Alpha relay\" items={[{id: \"inspect\", label: \"Inspect\"}]} onAction={handleAction}>EQ-01</ContextMenu>"
}