Context Menu
Cursor-positioned actions for a target, powered by the shared Menu component.
ContextMenu combines a target area with the existing Menu component. It renders the menu in a portal, opens on a right-click, positions it near the pointer, keeps it inside the viewport, and supports keyboard invocation with Shift + F10 or the Context Menu key.
Basic usage
<ContextMenu
aria-label="Project actions"
entries={[
{ key: 'open', label: 'Open' },
{ key: 'rename', label: 'Rename', shortcut: 'F2' },
{ type: 'divider' },
{ key: 'delete', label: 'Delete', danger: true },
]}
onSelect={(key) => handleProjectAction(project, key)}
>
<ProjectRow project={project} />
</ContextMenu>The target can be a card, table row, list item, canvas region, button, or any other React content. entries uses the same MenuEntry[] type as Menu, including icons, descriptions, images, shortcuts, labels, dividers, disabled items, danger actions, and nested submenus.
Keyboard and dismissal
Focus the target and press Shift + F10 or the keyboard Context Menu key to open the menu without a pointer. Escape closes it and returns focus to the target. By default, a mousedown outside the menu, scrolling, or menu selection closes it; control these behaviors with closeOnOutsideClick, closeOnEscape, and closeOnSelect.
<ContextMenu
entries={actions}
closeOnSelect={false}
menuProps={{ selectable: true, selectedKeys: selectedKeys }}
>
<button type="button">Focus and use Shift + F10</button>
</ContextMenu>Controlled state and positioning
Use open and onOpenChange when an application owns the menu lifecycle. The callback receives the next state and the pointer position. position can provide a controlled anchor point in viewport coordinates; otherwise the component uses the most recent pointer or keyboard target position.
<ContextMenu
open={open}
position={{ x: 240, y: 180 }}
onOpenChange={(nextOpen, point) => {
setOpen(nextOpen);
if (point) setLastPoint(point);
}}
entries={actions}
>
<CanvasRegion />
</ContextMenu>offset controls the distance between the pointer and the menu. The component clamps both coordinates so that the menu remains visible inside the viewport. Use menuWidth or menuProps to customize the shared Menu size, variant, keyboard behavior, selection, max height, and footer. Set preventDefault={false} when the browser's native context menu should remain available, or disabled to disable the context-menu behavior for a target.
API Reference
| Property | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Target that receives context-menu and keyboard events. |
entries | MenuEntry[] | — | Items passed to the shared Menu. |
aria-label | string | 'Context menu' | Accessible menu name. |
className | string | — | Class name applied to the target wrapper. |
id | string | Generated | ID applied to the portalled menu popover. |
onSelect | (key, item) => void | — | Called when a menu item is selected. |
open | boolean | — | Controlled open state. |
defaultOpen | boolean | false | Initial uncontrolled open state. |
onOpenChange | (open, position?) => void | — | Called when the menu opens or closes; opening provides the trigger position. |
position | { x: number; y: number } | Last trigger point | Controlled viewport coordinates used as the menu anchor. |
offset | number | 4 | Gap between trigger point and menu in pixels. |
menuWidth | number | string | 280 | Menu width. |
menuProps | MenuProps without entries, onSelect, aria-label, width | — | Additional shared Menu options. |
closeOnEscape | boolean | true | Closes on Escape. |
closeOnOutsideClick | boolean | true | Closes when clicking outside. |
closeOnSelect | boolean | true | Closes after an item is selected. |
preventDefault | boolean | true | Prevents the browser context menu. |
disabled | boolean | false | Disables context-menu behavior. |
Use Menu directly when the menu is already visible in the page, and ButtonMenu when a visible button should own the trigger. Use ContextMenu when actions are tied to a target and should appear at the pointer location.