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