Context Menu
The actions menu that opens where you right-click. Built on Radix ContextMenu for keyboard navigation and a11y; it shares its surface and density with Dropdown Menu, but anchors to the cursor.
Installation#
Usage#
import {
ContextMenu, ContextMenuTrigger, ContextMenuContent,
ContextMenuItem, ContextMenuSeparator, ContextMenuShortcut,
} from "@/components/ui/context-menu"
import { Card } from "@/components/ui/card"
import { Copy, PencilSimple, Trash } from "koala-icons"
export function Example() {
return (
<ContextMenu>
<ContextMenuTrigger asChild>
<Card className="w-72 select-none p-6">Right-click me</Card>
</ContextMenuTrigger>
<ContextMenuContent className="w-48">
<ContextMenuItem>
<PencilSimple /> Rename
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<Copy /> Duplicate
<ContextMenuShortcut>⌘D</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem variant="destructive">
<Trash /> Delete
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)
}Anatomy#
import {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuLabel,
ContextMenuGroup,
ContextMenuItem,
ContextMenuShortcut,
ContextMenuCheckboxItem,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubTrigger,
ContextMenuSubContent,
} from "@/components/ui/context-menu"
<ContextMenu>
<ContextMenuTrigger />
<ContextMenuContent>
<ContextMenuLabel />
<ContextMenuGroup>
<ContextMenuItem>
<ContextMenuShortcut />
</ContextMenuItem>
</ContextMenuGroup>
<ContextMenuCheckboxItem />
<ContextMenuRadioGroup>
<ContextMenuRadioItem />
</ContextMenuRadioGroup>
<ContextMenuSeparator />
<ContextMenuSub>
<ContextMenuSubTrigger />
<ContextMenuSubContent />
</ContextMenuSub>
</ContextMenuContent>
</ContextMenu>Any surface as the trigger#
Wrap any element with ContextMenuTrigger and asChild; the menu opens at the cursor anywhere inside it. An image is the classic right-click target.
Checkbox items#
Toggle independent options straight from the menu. ContextMenuCheckboxItem closes the menu when toggled, like any row; pass closeOnSelect={false} to flip several in one pass.
Switch items#
Pass variant="switch" to a checkbox item for an option that applies the moment it flips. The menu stays open when it is toggled, so a few can be set before it closes; the row is still a menu checkbox, with the switch as its drawing.
Radio items#
A mutually exclusive choice lives in a ContextMenuRadioGroup with a shared value; the active row shows a check.
Groups, submenus & shortcuts#
Section items with ContextMenuGroup, nest a ContextMenuSub, and add right-aligned hints with ContextMenuShortcut (decorative; wire the real binding in your app). Use inset to align items that have no leading icon.
API reference#
ContextMenu
The root, Radix ContextMenu.Root; renders no DOM. Every part that renders an element accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| onOpenChange | (open: boolean) => void | - | Called when the menu opens or closes. A context menu has no controlled open: the gesture opens it. |
| modal | boolean | true | Block outside pointer interaction and hide the rest of the page from screen readers while open. |
| dir | "ltr" | "rtl" | - | Reading direction for keyboard navigation and submenu placement. |
ContextMenuTrigger
The area that opens the menu on right-click or long-press, Radix ContextMenu.Trigger. Renders a <span> and forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Merge onto your own element (a Card, a figure, a row) instead of rendering a <span>. |
| disabled | boolean | false | Let the native browser context menu open instead. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
| data-disabled | Present when disabled. |
ContextMenuContent
The menu, Radix ContextMenu.Content portalled to the body and anchored to the cursor; forwards all its props. Declares --surface, so a nested control blends with the menu.
| Prop | Type | Default | Description |
|---|---|---|---|
| density | "comfortable" | "compact" | "compact" | Row height for every row inside, 36px or 32px. Unset, the rows follow the nearest DensityProvider. |
| updatePositionStrategy | "optimized" | "always" | "always" | always re-anchors an open menu to each new right-click point; optimized only re-measures on scroll and resize. |
| collisionPadding | number | 8 | Distance in pixels kept from the viewport edge; also shrinks the width the menu is capped at. |
| alignOffset | number | 0 | Offset in pixels from the cursor along the alignment axis. |
| avoidCollisions | boolean | true | Flip and shift to stay on screen. |
| loop | boolean | false | Wrap keyboard focus from the last row to the first. |
| onCloseAutoFocus | (event: Event) => void | - | Called as focus returns on close; preventDefault() to keep it elsewhere. |
| onEscapeKeyDown | (event: KeyboardEvent) => void | - | Called on Escape; preventDefault() to keep the menu open. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
| data-side | The side of the cursor the menu opened on, after collisions. |
| data-align | "start", "center" or "end". |
| CSS variable | Description |
|---|---|
| --radix-popper-transform-origin | The cursor-side origin the menu grows from. |
| --radix-context-menu-content-available-width | Width left beside the cursor; the menu's max-width. |
| --radix-context-menu-content-available-height | Height left between the cursor and the viewport edge. |
| --surface | Set to var(--popover), read by nested Inputs and Selects. |
ContextMenuItem
One action row, Radix ContextMenu.Item; forwards all its props. A leading Koala icon is muted and sized to 20px.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "destructive" | "default" | destructive colours the label, icon and highlight red. |
| inset | boolean | false | Indent the label to line up with siblings that lead with an icon. |
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
| disabled | boolean | false | Skip the row in keyboard navigation and ignore clicks. |
| onSelect | (event: Event) => void | - | Called when the row is chosen; preventDefault() to keep the menu open. |
| textValue | string | - | Text used for typeahead when the row's content is not plain text. |
| asChild | boolean | false | Merge onto your own element, such as a link. |
| Data attribute | Description |
|---|---|
| data-highlighted | Present while the row has keyboard or pointer focus. |
| data-disabled | Present when the row is disabled. |
| data-inset | Present when inset is set. |
ContextMenuCheckboxItem
A toggle row, Radix ContextMenu.CheckboxItem (menuitemcheckbox); forwards all its props. Shows an ink check on the right, or a mini Switch.
| Prop | Type | Default | Description |
|---|---|---|---|
| checked | boolean | "indeterminate" | - | The checked state. |
| onCheckedChange | (checked: boolean) => void | - | Called when the row is toggled. |
| variant | "check" | "switch" | "check" | How the state reads: the check, or a Switch drawing for a setting that applies the moment it flips. |
| closeOnSelect | boolean | true | Close the menu when the row is toggled. Defaults to false for variant="switch". |
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
| disabled | boolean | false | Skip the row and ignore clicks. |
| onSelect | (event: Event) => void | - | Called when the row is chosen. |
| Data attribute | Description |
|---|---|
| data-state | "checked", "unchecked" or "indeterminate". |
| data-highlighted | Present while the row has keyboard or pointer focus. |
| data-disabled | Present when the row is disabled. |
ContextMenuRadioGroup
Holds one exclusive choice, Radix ContextMenu.RadioGroup. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The selected item's value. |
| onValueChange | (value: string) => void | - | Called when another item is chosen. |
ContextMenuRadioItem
One option in a radio group, Radix ContextMenu.RadioItem (menuitemradio); the selected row shows the same ink check as a checkbox row.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The value this row selects. |
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
| disabled | boolean | false | Skip the row and ignore clicks. |
| onSelect | (event: Event) => void | - | Called when the row is chosen; preventDefault() to keep the menu open. |
| Data attribute | Description |
|---|---|
| data-state | "checked" or "unchecked". |
| data-highlighted | Present while the row has keyboard or pointer focus. |
| data-disabled | Present when the row is disabled. |
ContextMenuLabel
A small muted section heading, Radix ContextMenu.Label; not focusable. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| inset | boolean | false | Indent to line up with rows that lead with an icon. |
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
ContextMenuSeparator
A hairline between sections, Radix ContextMenu.Separator, stopping at the menu's ring. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
ContextMenuGroup
Groups related rows for assistive tech, Radix ContextMenu.Group; unstyled. Forwards all native <div> props.
ContextMenuShortcut
A right-aligned shortcut hint, Koala's Kbd at size="sm"; decorative, it binds nothing. Forwards all Kbd props.
ContextMenuSub
The root of a nested menu, Radix ContextMenu.Sub; renders no DOM.
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | - | Controlled open state. |
| defaultOpen | boolean | false | Initial open state when uncontrolled. |
| onOpenChange | (open: boolean) => void | - | Called when the submenu opens or closes. |
ContextMenuSubTrigger
The row that opens a submenu, Radix ContextMenu.SubTrigger, with a caret on the right; forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| inset | boolean | false | Indent to line up with rows that lead with an icon. |
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
| disabled | boolean | false | Skip the row and keep the submenu closed. |
| textValue | string | - | Text used for typeahead when the row's content is not plain text. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
| data-highlighted | Present while the row has keyboard or pointer focus. |
| data-disabled | Present when the row is disabled. |
ContextMenuSubContent
The nested menu, Radix ContextMenu.SubContent; same surface as the content, and it animates out on close. Forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| density | "comfortable" | "compact" | "compact" | Row height, 36px or 32px. Falls back to the menu's density, then the nearest DensityProvider. |
| sideOffset | number | 0 | Gap in pixels between the trigger row and the submenu. |
| alignOffset | number | 0 | Offset in pixels along the alignment axis. |
| collisionPadding | number | 0 | Distance in pixels kept from the viewport edge. |
| loop | boolean | false | Wrap keyboard focus from the last row to the first. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
| data-side | The side the submenu opened on, after collisions. |
| data-align | "start", "center" or "end". |
| CSS variable | Description |
|---|---|
| --radix-popper-transform-origin | The trigger-side origin the submenu grows from. |
ContextMenuPortal
Radix ContextMenu.Portal. ContextMenuContent already portals itself, so you rarely need it.
| Prop | Type | Default | Description |
|---|---|---|---|
| container | HTMLElement | null | document.body | Where to portal the content. |