Light

Context Menu

Displays a menu located at the pointer, triggered by a right-click or a long-press.

Spec · from metadata

When to use

  • Right-click context menus on canvas elements, cards, or list items
  • Custom right-click menus replacing the browser default
  • Contextual actions scoped to a specific element or region

When not to use

  • Button-triggered action menus -- use DropdownMenu instead
  • App-level persistent menu bars -- use Menubar instead
  • Touch-only interfaces -- right-click is not available; use DropdownMenu with a trigger button
  • Navigation links -- use NavigationMenu

Variants

PropValuesDefaultDescription
variant (on ContextMenuItem)defaultdestructivedefaultControls item styling. 'destructive' applies red text and destructive focus styles.

Anti-patterns

Avoid// Using ContextMenu for a button-triggered actions menu
<ContextMenu>
  <ContextMenuTrigger>
    <Button>Actions</Button>
  </ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>Edit</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>
Prefer<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button>Actions</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem>Edit</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

ContextMenu is for right-click. Button-triggered menus should use DropdownMenu, which opens on click and has proper trigger semantics.

Avoid<ContextMenu>
  <ContextMenuTrigger>
    {/* invisible trigger area */}
  </ContextMenuTrigger>
  <ContextMenuContent>...</ContextMenuContent>
</ContextMenu>
Prefer<ContextMenu>
  <ContextMenuTrigger className="h-[200px] w-full rounded-md border border-dashed">
    Right-click anywhere in this area
  </ContextMenuTrigger>
  <ContextMenuContent>...</ContextMenuContent>
</ContextMenu>

The ContextMenuTrigger must have a visible, perceivable area so users know where right-click is available.

Accessibility

  • Screen readerAnnounced as a menu. Same ARIA semantics as DropdownMenu. Note: right-click context menus are not discoverable by screen reader users -- ensure actions are also available through other means.
  • ContrastUses bg-popover/text-popover-foreground. Destructive items use text-destructive.

Token bindings

TokenCategoryUsage
bg-popovercolorMenu background
text-popover-foregroundcolorMenu text
bg-accentcolorFocused item background
text-accent-foregroundcolorFocused item text
text-destructivecolorDestructive item text
text-muted-foregroundcolorIcons and shortcuts
bg-bordercolorSeparator
shadow-mdshadowMenu elevation
rounded-mdradiusMenu border radius

Import

import {
  ContextMenu,
  ContextMenuTrigger,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuCheckboxItem,
  ContextMenuRadioItem,
  ContextMenuRadioGroup,
  ContextMenuLabel,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubTrigger,
  ContextMenuSubContent,
} from "@timelycare/helix-ui"

Props

interface ContextMenuItemProps {
  disabled?: boolean
  onSelect?: () => void
  className?: string
  children: React.ReactNode
}

interface ContextMenuCheckboxItemProps extends ContextMenuItemProps {
  checked?: boolean
  onCheckedChange?: (checked: boolean) => void
}

interface ContextMenuRadioItemProps {
  value: string
  disabled?: boolean
  children: React.ReactNode
}

Design Tokens

Container

PropertyTokenValue
Backgroundbg-popoverwhite
Borderborder1px solid border color
Border radiusrounded-md8px
Paddingp-14px
Shadowshadow-mdMulti-layer drop shadow
Min width256px

Item Sizing

PropertyTokenValue
Height32px
Padding (Level 1)px-2 py-1.58px horizontal, 6px vertical
Padding (Level 2)pl-8 pr-2 py-1.532px left (for indicator), 8px right, 6px vertical
Border radiusrounded-sm6px

Typography

ElementSizeColorWeight
Item texttext-sm (14px)text-popover-foregroundnormal
Shortcut texttext-xs (12px)text-muted-foregroundnormal
Label/Titletext-sm (14px)text-foregroundsemibold

Separator

PropertyTokenValue
Heighth-28px
Paddingpy-14px vertical
Border colorborder#e4e4e7

Item Variants

VariantUse ForTailwind
defaultStandard menu actionstext-popover-foreground
checkboxToggleable optionstext-popover-foreground + checkmark indicator
radioSingle-select from grouptext-popover-foreground + circle indicator

Choosing a Variant

  • Use default for actions (Back, Forward, Save)
  • Use checkbox for independent toggles (Show Bookmarks)
  • Use radio for mutually exclusive choices (Select User)

States

StateImplementation
Defaulttext-popover-foreground
Hoverbg-accent text-accent-foreground (automatic)
Focusfocus:bg-accent focus:text-accent-foreground
Disabledopacity-50 pointer-events-none

Icons & Shortcuts

Indicator icons: size-4 (16px × 16px) Indicator position: 8px from left edge Shortcut spacing: ml-auto text-xs text-muted-foreground SubTrigger chevron: size-4 ChevronRight icon

<ContextMenuItem>
  Back
  <ContextMenuShortcut>⌘[</ContextMenuShortcut>
</ContextMenuItem>

Common Patterns

Basic Menu

<ContextMenu>
  <ContextMenuTrigger>Right click here</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>Back</ContextMenuItem>
    <ContextMenuItem>Forward</ContextMenuItem>
    <ContextMenuItem>Reload</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

With Submenu

<ContextMenuSub>
  <ContextMenuSubTrigger>More Tools</ContextMenuSubTrigger>
  <ContextMenuSubContent>
    <ContextMenuItem>Save Page As</ContextMenuItem>
    <ContextMenuItem>Developer Tools</ContextMenuItem>
  </ContextMenuSubContent>
</ContextMenuSub>

Checkbox & Radio Items

<ContextMenuCheckboxItem checked={showBookmarks} onCheckedChange={setShowBookmarks}>
  Show Bookmarks
</ContextMenuCheckboxItem>

<ContextMenuRadioGroup value={person} onValueChange={setPerson}>
  <ContextMenuLabel>People</ContextMenuLabel>
  <ContextMenuRadioItem value="pedro">Pedro Duarte</ContextMenuRadioItem>
  <ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
</ContextMenuRadioGroup>

With Sections

<ContextMenuContent>
  <ContextMenuItem>Back</ContextMenuItem>
  <ContextMenuItem disabled>Forward</ContextMenuItem>
  <ContextMenuSeparator />
  <ContextMenuLabel>People</ContextMenuLabel>
  <ContextMenuRadioGroup value={person}>
    <ContextMenuRadioItem value="pedro">Pedro Duarte</ContextMenuRadioItem>
  </ContextMenuRadioGroup>
</ContextMenuContent>

Accessibility

  • Built on Radix ContextMenu — handles keyboard and focus automatically
  • Keyboard: right-click or Shift+F10 to open, Arrow keys to navigate, Enter to select, Escape to close
  • Screen reader: announces menu role and item count
  • Typeahead search supported within menu items

Gotchas

ProblemSolution
Menu not openingWrap trigger element in ContextMenuTrigger
Submenu not showingUse ContextMenuSub wrapper with SubTrigger + SubContent
Radio items not groupedWrap in ContextMenuRadioGroup with shared value
Shortcut not aligned rightUse ContextMenuShortcut component (has ml-auto)

See Also


Last updated: February 9, 2026