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.

Preview

No preview output yet.

live.tsxReady

Framer Motion

Right-click triggered menu with root and submenu content animated through Framer Motion.

Preview

No preview output yet.

live.tsxReady

Anatomy

tsx
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.

PropTypeDefault
areaRef*
React.RefObject<HTMLElement>—
onValueChange
(value: string) => void—
offset
{ x: number; y: number }—
open
boolean—
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void—

Content

Floating menu container that portals to the body. Manages item registration, active-item tracking, and keyboard navigation.

PropTypeDefault
asChild
booleanfalse
AttributeValues
rolemenu
aria-activedescendantID 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.

PropTypeDefault
value*
string—
disabled
booleanfalse
AttributeValues
rolemenuitem
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.

PropTypeDefault
offset
{ x: number; y: number }—
open
boolean—
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void—

SubTrigger

A menu item that opens a nested submenu on hover or keyboard.

AttributeValues
rolemenuitem
aria-haspopupmenu
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.

PropTypeDefault
asChild
booleanfalse
AttributeValues
rolemenu
aria-activedescendantID of the currently active submenu item

Group

Groups related menu items and associates them with a Label.

AttributeValues
rolegroup
aria-labelledbyID of the child Label

Label

Accessible label for a Group.

Separator

Visual divider between menu sections.

AttributeValues
roleseparator

Keyboard

ShortcutAction
↓Move focus to the next item (wraps to first).
↑Move focus to the previous item (wraps to last).
HomeMove focus to the first item.
EndMove focus to the last item.
Enter/SpaceSelect the focused item and close the menu.
EscClose the menu.
→Open a submenu when a SubTrigger is focused.
←Close the current submenu and return focus to the SubTrigger.
TabClose the menu.

Accessibility

The Context Menu component implements the WAI-ARIA Menu pattern:

  • Content renders as role="menu" and tracks the active item via aria-activedescendant.
  • Each Item exposes role="menuitem" with roving tabindex and data-active for the focused state.
  • SubTrigger carries aria-haspopup="menu" and aria-expanded to announce submenu availability.
  • SubContent renders as role="menu" with its own aria-activedescendant.
  • Group uses role="group" with aria-labelledby pointing to the child Label.
  • Separator uses role="separator" for proper semantics between sections.
  • Navigation is cyclic and disabled items are skipped automatically.
Previous
Command