ContextMenu
The right-click sibling of DropdownMenu — a Radix-backed cursor-anchored menu with leading icons, Kbd shortcut hints, checkbox/radio rows, nested submenus, and a danger ramp for destructive items, composed via statics.
Import
import { Eye, Pin, Tag, Trash2 } from "@roger-emerson/ploxum-icons";
import { ContextMenu } from "@roger-emerson/ploxum-primitives";Usage
<ContextMenu>
<ContextMenu.Trigger asChild>
<div className="venue-zone">Gate 4 — right-click for actions</div>
</ContextMenu.Trigger>
<ContextMenu.Content width={208}>
<ContextMenu.Label>Gate 4</ContextMenu.Label>
<ContextMenu.Item icon={<Eye size={14} strokeWidth={1.5} />} onSelect={focus} shortcut="⌘[">
Focus zone
</ContextMenu.Item>
<ContextMenu.Item icon={<Tag size={14} strokeWidth={1.5} />} onSelect={tag}>
Add tag…
</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.CheckboxItem checked={pinned} onCheckedChange={setPinned}>
Pin to overlay
</ContextMenu.CheckboxItem>
<ContextMenu.Separator />
<ContextMenu.Item destructive icon={<Trash2 size={14} strokeWidth={1.5} />} onSelect={remove}>
Remove zone
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu>Props
ContextMenu (root)
| Prop | Type | Default | Description |
|---|---|---|---|
onOpenChange | (open: boolean) => void | — | Open-state callback. The menu opens on right-click — there is no controlled open. |
children | ReactNode | — | Trigger + Content. |
ContextMenu.Trigger
Radix ContextMenu.Trigger passthrough — use asChild to wrap the surface that should respond to right-click (a map zone, table row, card, etc.). The whole trigger area is the hit target.
ContextMenu.Content
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | — | Fixed width in px. Defaults to content-sized with a 180px floor. |
className | string | — | Merged onto the panel. |
Positions at the cursor, not relative to the trigger — so there are no align / sideOffset props. Also accepts the remaining Radix ContextMenu.Content props (collisionPadding, onCloseAutoFocus, …).
ContextMenu.Item
| Prop | Type | Default | Description |
|---|---|---|---|
onSelect | (event: Event) => void | — | Fires on selection (click or Enter). |
icon | ReactNode | — | Leading icon. Inherits the item's text color, including danger on destructive items. |
shortcut | string | — | Keyboard shortcut hint, rendered right-aligned in a Kbd. |
destructive | boolean | — | Renders the item in the danger ramp. Always pair with icon. |
disabled | boolean | — | Dims the item and removes it from pointer/keyboard flow. |
className | string | — | Merged onto the item. |
ContextMenu.CheckboxItem
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Checked state — controlled when paired with onCheckedChange. |
onCheckedChange | (checked: boolean) => void | — | Fires on toggle. |
onSelect | (event: Event) => void | — | Fires on selection, before the toggle resolves. |
disabled | boolean | — | Dims the row and removes it from pointer/keyboard flow. |
className | string | — | Merged onto the row. |
children | ReactNode | — | Row label. Amber check indicator renders in the leading gutter. |
ContextMenu.RadioGroup
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Selected item value (controlled with onValueChange). |
onValueChange | (value: string) => void | — | Fires when the selection changes. |
children | ReactNode | — | RadioItems, optionally with a Label. |
ContextMenu.RadioItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Required. Identifies the item within its RadioGroup. |
disabled | boolean | — | Dims the row and removes it from pointer/keyboard flow. |
className | string | — | Merged onto the row. |
children | ReactNode | — | Row label. Amber dot indicator renders in the leading gutter. |
ContextMenu.Sub / ContextMenu.SubContent
ContextMenu.Sub is the Radix Sub passthrough wrapping a nested branch. ContextMenu.SubContent mirrors ContextMenu.Content — it takes the same width, className, and remaining Radix SubContent props.
ContextMenu.SubTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
icon | ReactNode | — | Leading icon. Inherits the item's text color. |
disabled | boolean | — | Dims the row and removes it from flow. |
className | string | — | Merged onto the row. |
children | ReactNode | — | Row label. A trailing chevron is appended automatically. |
ContextMenu.Separator / ContextMenu.Label
Both take only className (Label also takes children). Label renders the uppercase tertiary group heading.
ContextMenu.Shortcut / ContextMenu.Group
ContextMenu.Shortcut renders a right-aligned Kbd for hand-composed rows (prefer the Item shortcut prop for standard items). ContextMenu.Group is the Radix Group passthrough for grouping related items under one Label for assistive tech.
Design rules
- Destructive items must pair
destructivewith anicon— the danger state reads by shape as well as color, never hue alone. shortcutis a hint chip only; the menu does not bind the key. Register the actual handler where the command lives (usually the CommandPalette's action map).- The panel animates a 200ms fade/scale on the
cubic-bezier(0.25, 1, 0.5, 1)ease — within the motion budget, no spring. Don't extend it.SubContentshares the same motion. - ContextMenu shares
DropdownMenu's overlay treatment — same overlay surface, border, and shadow — so the two menus read as one family. - ContextMenu is a menu: focus moves into it on open and returns to the trigger on close. For non-modal anchored surfaces that must not trap focus, use
Popover(heuristic 9).
