VisorVisor
ComponentsOverlay

Context Menu

A menu that appears on right-click or long-press. Built on Radix UI with support for nested submenus, checkboxes, radio items, shortcuts, and disabled states.

Basic Context Menu

Right click anywhere here

With Keyboard Shortcuts

Right click for shortcuts

Destructive Items

Right click to see destructive items

Disabled Items

Right click to see disabled items

Checkbox Items

Right click for view options

Radio Items

Right click for layout options

Nested Submenus

Right click for nested menus

Installation

npx visor add context-menu

This copies two files into your project:

  • components/ui/context-menu/context-menu.tsx — the component
  • components/ui/context-menu/context-menu.module.css — the styles

Usage

import {
  ContextMenu,
  ContextMenuTrigger,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuLabel,
} from '@/components/ui/context-menu/context-menu';

export default function Example() {
  return (
    <ContextMenu>
      <ContextMenuTrigger>Right click here</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuLabel>Actions</ContextMenuLabel>
        <ContextMenuItem>Edit</ContextMenuItem>
        <ContextMenuItem>Duplicate</ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem variant="destructive">Delete</ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  );
}

API Reference

ContextMenuProps

PropTypeDefaultDescription
variant'default' | 'destructive''default'Visual style of ContextMenuItem — use "destructive" for dangerous actions.
insetbooleanfalseAdds left padding to ContextMenuItem, ContextMenuLabel, or ContextMenuSubTrigger to align with items that have icons.
disabledbooleanfalseDisables the menu item, making it non-interactive and visually muted.
checkedboolean | "indeterminate"Controlled checked state for ContextMenuCheckboxItem.
onCheckedChange(checked: boolean) => voidCallback when the checked state of a ContextMenuCheckboxItem changes.
valuestringValue for ContextMenuRadioItem. The selected value is managed by ContextMenuRadioGroup.
onValueChange(value: string) => voidCallback on ContextMenuRadioGroup when the selected radio value changes.
onSelect(event: Event) => voidCallback when a ContextMenuItem is selected. Call event.preventDefault() to keep the menu open.
classNamestringAdditional CSS class names to merge onto the element.

Sub-components

ComponentElementPurpose
ContextMenuRootContext provider (wraps Radix ContextMenu.Root)
ContextMenuTriggerAny elementRight-click target area
ContextMenuContentFloating panelContainer for menu items
ContextMenuGroupGroupGroups related items
ContextMenuLabelLabelNon-interactive section heading
ContextMenuItemItemClickable menu item
ContextMenuCheckboxItemItemToggleable checkbox item
ContextMenuRadioGroupGroupContainer for radio items
ContextMenuRadioItemItemMutually exclusive selection item
ContextMenuSeparator<hr>Visual divider between groups
ContextMenuShortcut<span>Keyboard shortcut label displayed on the right
ContextMenuSubRootWrapper for a nested submenu
ContextMenuSubTriggerItemTrigger that opens a submenu
ContextMenuSubContentFloating panelContainer for submenu items

Accessibility

  • Built on Radix UI ContextMenu — ARIA roles (menu, menuitem, menuitemcheckbox, menuitemradio) are applied automatically.
  • Keyboard navigation: ArrowUp/ArrowDown to move between items, ArrowRight to open submenus, ArrowLeft to close them, Escape to dismiss.
  • Focus is trapped within the open menu and restored to the trigger on close.
  • Disabled items use aria-disabled and are not keyboard-reachable.
  • Shortcut text is visible to screen readers — use aria-hidden if the shortcut text would be redundant.