Context Menu
A Context Menu allows users to open a list of actions at the pointer with a right click or a long press.
When to Use
- to repeat, at the pointer, the actions that a row, a card, or a file already shows, such as “Open”, “Duplicate”, “Rename”, and “Delete”
- to change the view settings of the area under the pointer, with checkbox and radio items, like “Show comments” or the row density
When Not to Use
- as the only way to reach an action (show the same actions in a Dropdown Menu)
- to wrap text that users copy, as the trigger sets
select-noneand its text can’t be selected
See Dropdown Menu for a comparison of the four menus.
Anatomy
A Context Menu has two parts. The trigger is the area that users right-click, and it has no style of its own. The popup contains the item types of a Dropdown Menu under the ContextMenu prefix.
Behavior
A Context Menu is modal: while it is open, Base UI locks the page’s scroll and prevents clicks from reaching the page. Unlike a Dropdown Menu, it has no modal prop.
By default, the menu opens at the pointer. If the menu doesn’t fit there, Base UI moves it. If you don’t set a width, the menu is as wide as its longest item, with a minimum width of 144px.
Accessibility
The roles of the items and the keys inside the menu are the same as in a Dropdown Menu.
Keyboard
| Key | Result |
|---|---|
ContextMenu on a control inside the trigger | Opens the menu at that control. Focus moves to the popup. |
ArrowDown, ArrowUp | Moves focus from the popup to the first or the last item |
Escape | Closes the menu. Focus returns to the control that had it. |
Tab | Does nothing. The menu stays open. |
Requirements
Keyboard users need a control on the page for each action in a Context Menu, as WCAG 2.2 SC 2.1.1 Keyboard requires. The trigger can’t receive focus, nothing on screen shows that the menu exists, and the keyboard opens it only from a control inside the trigger.
ContextMenuContent has role="menu" and no accessible name unless you set one.
Installation
npx shadcn@latest add @summit/context-menuUsage
ContextMenuTrigger renders a div around its children. To run an action from an item, pass it to onClick.
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@/components/ui/context-menu';
<ContextMenu>
<ContextMenuTrigger className="rounded-xl border p-4">INV-2026-014</ContextMenuTrigger>
<ContextMenuContent className="w-48">
<ContextMenuItem onClick={open}>Open</ContextMenuItem>
<ContextMenuItem onClick={duplicate}>Duplicate</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem variant="destructive" onClick={remove}>
Delete
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>;With a Dropdown Menu
To show the same actions in a Dropdown Menu, list the actions once and render them in both menus. Place the Dropdown Menu next to the Context Menu, not inside its trigger. See the Base UI Context Menu documentation for this pattern.
const actions = [
{ label: 'Open', run: open },
{ label: 'Duplicate', run: duplicate },
];
<div className="relative">
<ContextMenu>
<ContextMenuTrigger className="rounded-xl border p-4">INV-2026-014</ContextMenuTrigger>
<ContextMenuContent className="w-40">
{actions.map(({ label, run }) => (
<ContextMenuItem key={label} onClick={run}>
{label}
</ContextMenuItem>
))}
</ContextMenuContent>
</ContextMenu>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="ghost"
size="icon-sm"
className="absolute end-2 top-2"
aria-label="Actions for INV-2026-014"
/>
}
>
<DotsThreeIcon weight="bold" />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
{actions.map(({ label, run }) => (
<DropdownMenuItem key={label} onClick={run}>
{label}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>;API Reference
Apart from ContextMenu, ContextMenuTrigger, and ContextMenuContent, the other exports are the parts of a Dropdown Menu under the ContextMenu prefix. variant and inset work as they do there.
The parts, except ContextMenuShortcut, accept the props of their matching parts in Base UI’s Context Menu. See the Base UI Context Menu documentation for open, onOpenChange, and disabled.
ContextMenuContent
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'top' | 'bottom' | 'left' | 'right' | 'inline-start' | 'inline-end' | 'inline-end' | The side of the pointer the menu opens on |
sideOffset | number | OffsetFunction | 0 | The gap between the pointer and the menu, in pixels |
align | 'start' | 'center' | 'end' | 'start' | The edge of the menu that is placed at the pointer |
alignOffset | number | OffsetFunction | 4 | A shift along that edge, in pixels |