Summit
ComponentsOverlays

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-none and 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

KeyResult
ContextMenu on a control inside the triggerOpens the menu at that control. Focus moves to the popup.
ArrowDown, ArrowUpMoves focus from the popup to the first or the last item
EscapeCloses the menu. Focus returns to the control that had it.
TabDoes 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.

DoShow the same actions in a Dropdown Menu so that keyboard users can reach them.
Don’tWhen a Context Menu is the only way to reach the actions, nothing on screen shows that the menu exists, and keyboard users have no control to open it from.

ContextMenuContent has role="menu" and no accessible name unless you set one.

Installation

npx shadcn@latest add @summit/context-menu

Usage

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.

With a Dropdown Menu
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

PropTypeDefaultDescription
side'top' | 'bottom' | 'left' | 'right' | 'inline-start' | 'inline-end''inline-end'The side of the pointer the menu opens on
sideOffsetnumber | OffsetFunction0The 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
alignOffsetnumber | OffsetFunction4A shift along that edge, in pixels

On this page