Navigation
Context Menu
A menu opened from a right click instead of a button.
When to use it: Use it for secondary actions on an object that already has a primary interaction.
Example
import { MenuItem, MenuList, MenuPopover, MenuSeparator } from "@comp0/react";
import { ContextMenu, ContextMenuTrigger } from "@comp0/react";
export function Example() {
return (
<ContextMenu>
<ContextMenuTrigger
tabIndex={0}
className="grid h-36 w-full max-w-md place-items-center rounded border border-dashed border-zinc-950/20 text-base text-zinc-500 outline-teal-600 select-none focus-visible:outline-2 data-open:border-teal-600 sm:text-sm dark:border-white/20 dark:text-zinc-400 dark:outline-teal-400 dark:data-open:border-teal-400"
>
Right-click here (or press Shift+F10)
</ContextMenuTrigger>
{/* The popover positions itself: the recorded pointer position arrives
as --comp0-context-menu-x/y px values on the popover element. */}
<MenuPopover className="fixed inset-auto top-(--comp0-context-menu-y) left-(--comp0-context-menu-x) m-0 w-44 rounded border-0 bg-white p-1 shadow-lg ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:shadow-none dark:ring-white/10">
<MenuList aria-label="Attachment actions">
<MenuItem
className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
value="download"
>
Download
</MenuItem>
<MenuItem
className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
value="rename"
>
Rename
</MenuItem>
<MenuSeparator className="my-1 h-px bg-zinc-950/10 dark:bg-white/10" />
<MenuItem
className="w-full rounded px-3 py-2.5 text-left text-base text-red-700 select-none hover:bg-red-50 sm:py-2 sm:text-sm dark:text-red-400 dark:hover:bg-red-950"
value="remove"
>
Remove
</MenuItem>
</MenuList>
</MenuPopover>
</ContextMenu>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
ContextMenu
Open-state provider without a trigger button; it records the pointer position and restores focus on close. Does not add a DOM element.
ContextMenuTrigger
The right-clickable area: a plain div that opens the menu at the pointer on contextmenu, or from the keyboard with Shift+F10. Owns a DOM element.
MenuPopover
Floating surface. The recorded position arrives as --comp0-context-menu-x/y px values on this element; anchor it yourself with position: fixed; left: var(--comp0-context-menu-x); top: var(--comp0-context-menu-y). Owns a DOM element.
MenuListOptional
Menu collection inside the positioned surface. Owns a DOM element.
MenuItem
Action item. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap the right-clickable area in ContextMenuTrigger inside a ContextMenu.
- 2
Add the supporting parts
Put a labelled MenuList inside MenuPopover; no button labels the list for you.
- 3
Make the behavior clear
Anchor the popover at the pointer yourself: position: fixed with left/top from the exposed --comp0-context-menu-x/y variables.
Exampletsx <ContextMenu><ContextMenuTrigger tabIndex={0}>Attachment</ContextMenuTrigger><MenuPopover style={{ position: "fixed", inset: "auto", margin: 0, left: "var(--comp0-context-menu-x)", top: "var(--comp0-context-menu-y)" }}><MenuList aria-label="Attachment actions"><MenuItem>Download</MenuItem></MenuList></MenuPopover></ContextMenu>
Keyboard
- ⇧F10
- Opens the menu at the focused element. · trigger area
- ☰
- Opens the menu at the focused element. · trigger area
- ↓
- Moves to the next item.
- ↑
- Moves to the previous item.
- ↵
- Activates the focused item.
- Esc
- Closes and restores focus to where it was.
- ⇥
- Closes the menu and moves on.
Forms and accessibility
No native form behavior.
Accessibility checklist
- Give the MenuList an aria-label; a context menu has no trigger button to borrow a name from.
- Keep the trigger area keyboard-reachable (tabIndex={0}) so Shift+F10 can open the menu without a mouse.
- Offer the same actions somewhere visible; right-click alone is not discoverable.
API reference
import { ContextMenu, ContextMenuTrigger, MenuItem, MenuList, MenuPopover } from "@comp0/react";ContextMenu
Context onlyOpen-state provider without a trigger button; it records the pointer position and restores focus on close.
| Prop | Type | Description |
|---|---|---|
open / defaultOpen | boolean | Controlled or initial open state. |
onToggle | (open: boolean) => void | Receives the next open state. |
ContextMenuTrigger
DOM elementThe right-clickable area: a plain div that opens the menu at the pointer on contextmenu, or from the keyboard with Shift+F10.
| Prop | Type | Description |
|---|---|---|
tabIndex | number | Give the area (or something inside it) a tab stop so keyboard users can reach the menu. |
MenuPopover
DOM elementFloating surface. The recorded position arrives as --comp0-context-menu-x/y px values on this element; anchor it yourself with position: fixed; left: var(--comp0-context-menu-x); top: var(--comp0-context-menu-y).
| Prop | Type | Description |
|---|---|---|
className / style | string | CSSProperties | Position the popover from the exposed CSS variables; no placement is applied for you. |
MenuList
OptionalDOM elementMenu collection inside the positioned surface.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the menu; required because no trigger button labels it. |
MenuItem
DOM elementAction item.
| Prop | Type | Description |
|---|---|---|
onClick | (event) => void | Runs the action; preventDefault keeps the menu open. |
value | string | Optional identity for typeahead and data-value. |
disabled | boolean | Disables the action. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
[data-open]on ContextMenuTrigger, MenuPopover
- The menu is open.
:popover-openon MenuPopover
- Native pseudo-class equivalent.
--comp0-context-menu-x/yon MenuPopover
- The recorded pointer position in px, for your positioning CSS.
:focus-visibleon MenuItem
- The item has visible keyboard focus.