Context Menu
A headless, accessible context menu triggered by right-click with keyboard navigation, submenus, and grouped items.
Features
- Right-click triggered
- Keyboard-first
- Submenu support
- Grouped items
- Roving tabindex
- Headless styling
Installation
npm install @ariaui/context-menu
Examples
Menu triggered by right-click (or keyboard). Supports items, submenus, and separators; style each part with className.
Default
Right-click triggered menu with nested submenus and grouped items.
No preview output yet.
Framer Motion
Right-click triggered menu with root and submenu content animated through Framer Motion.
No preview output yet.
Anatomy
import * as ContextMenu from "@ariaui/context-menu";
export default function Example() {
return (
<ContextMenu.Root>
<ContextMenu.Content>
<ContextMenu.Item />
<ContextMenu.Separator />
<ContextMenu.Sub>
<ContextMenu.SubTrigger />
<ContextMenu.SubContent>
<ContextMenu.Item />
</ContextMenu.SubContent>
</ContextMenu.Sub>
<ContextMenu.Group>
<ContextMenu.Label />
</ContextMenu.Group>
</ContextMenu.Content>
</ContextMenu.Root>
);
}
API Reference
Root
Wraps the context menu and owns open/close state. Attaches a context-menu listener to the element referenced by areaRef.
| Prop | Type | Default |
|---|---|---|
areaRef* | React.RefObject<HTMLElement> | — |
onValueChange | (value: string) => void | — |
offset | { x: number; y: number } | — |
open | boolean | — |
defaultOpen | boolean | false |
onOpenChange | (open: boolean) => void | — |
Content
Floating menu container that portals to the body. Manages item registration, active-item tracking, and keyboard navigation.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
| Attribute | Values |
|---|---|
| role | menu |
| aria-activedescendant | ID of the currently active item |
| [data-focused] | "true" when content container is focused, "false" when an item is focused |
Item
A selectable menu item. Fires onValueChange on click, Enter, or Space.
| Prop | Type | Default |
|---|---|---|
value* | string | — |
disabled | boolean | false |
| Attribute | Values |
|---|---|
| role | menuitem |
| tabindex | "0" when focused, "-1" otherwise |
| [data-active] | Present when focused |
| [data-disabled] | Present when disabled |
Sub
State container for a submenu. Wraps SubTrigger and SubContent.
| Prop | Type | Default |
|---|---|---|
offset | { x: number; y: number } | — |
open | boolean | — |
defaultOpen | boolean | false |
onOpenChange | (open: boolean) => void | — |
SubTrigger
A menu item that opens a nested submenu on hover or keyboard.
| Attribute | Values |
|---|---|
| role | menuitem |
| aria-haspopup | menu |
| aria-expanded | "true" when submenu is open |
| tabindex | "0" when focused, "-1" otherwise |
| [data-active] | Present when focused |
SubContent
Floating container for submenu items. Positioned relative to SubTrigger.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
| Attribute | Values |
|---|---|
| role | menu |
| aria-activedescendant | ID of the currently active submenu item |
Group
Groups related menu items and associates them with a Label.
| Attribute | Values |
|---|---|
| role | group |
| aria-labelledby | ID of the child Label |
Label
Accessible label for a Group.
Separator
Visual divider between menu sections.
| Attribute | Values |
|---|---|
| role | separator |
Keyboard
| Shortcut | Action |
|---|---|
| ↓ | Move focus to the next item (wraps to first). |
| ↑ | Move focus to the previous item (wraps to last). |
| Home | Move focus to the first item. |
| End | Move focus to the last item. |
| Enter/Space | Select the focused item and close the menu. |
| Esc | Close the menu. |
| → | Open a submenu when a SubTrigger is focused. |
| ← | Close the current submenu and return focus to the SubTrigger. |
| Tab | Close the menu. |
Accessibility
The Context Menu component implements the WAI-ARIA Menu pattern:
Contentrenders asrole="menu"and tracks the active item viaaria-activedescendant.- Each
Itemexposesrole="menuitem"with rovingtabindexanddata-activefor the focused state. SubTriggercarriesaria-haspopup="menu"andaria-expandedto announce submenu availability.SubContentrenders asrole="menu"with its ownaria-activedescendant.Groupusesrole="group"witharia-labelledbypointing to the childLabel.Separatorusesrole="separator"for proper semantics between sections.- Navigation is cyclic and disabled items are skipped automatically.