Skip to content
garn

Context Menu

A menu of actions opened by right-click or long-press on a target.

Stablev0.4.2added in v0.1.0@garn/ui/context-menu
On this page

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add context-menu
First time? Set up garn in your project
Registry
context-menu
Deps
@radix-ui/react-context-menulucide-react
Registry deps
utils

Import

import { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent } from "@garn/ui/context-menu";

Anatomy

The parts this component is built from — compose them to assemble it.

ContextMenuRequired

Root — owns the open state. No DOM of its own.

ContextMenuTriggerRequired[data-slot="context-menu-trigger"]

The target area; right-click / long-press inside it opens the menu.

ContextMenuContentRequired[data-slot="context-menu-content"]

The portaled menu surface (positioned at the pointer).

ContextMenuItem[data-slot="context-menu-item"]

An action item; inset aligns it with checkbox/radio items.

ContextMenuCheckboxItem[data-slot="context-menu-checkbox-item"]

A toggleable item (controlled via checked + onCheckedChange).

ContextMenuRadioGroup

Groups radio items; owns the selected value.

ContextMenuRadioItem[data-slot="context-menu-radio-item"]

A single-select item inside a RadioGroup.

ContextMenuLabel[data-slot="context-menu-label"]

A non-interactive section heading; inset for alignment.

ContextMenuSeparator[data-slot="context-menu-separator"]

Divider between groups.

ContextMenuShortcut[data-slot="context-menu-shortcut"]

Right-aligned shortcut hint (display only).

ContextMenuSub

A submenu container (Sub / SubTrigger / SubContent).

ContextMenuSubTrigger[data-slot="context-menu-sub-trigger"]

Opens a submenu; inset for alignment.

ContextMenuSubContent[data-slot="context-menu-sub-content"]

The nested submenu surface.

Props

The component's public props and their types.

No component-specific props — this is a thin wrapper over its native element.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="context-menu-content"]

States

SelectorState
[data-state=open]open
[data-highlighted]item-highlighted
[data-disabled]item-disabled
PropertyTokenTier
itemMinHeight--garn-control-h-mdsemantic · air

When to use

Offer actions contextual to an element via right-click / long-press — canvas objects, file/list rows, rich editors.

Reach for something else when

  • The primary or only access to actions (right-click isn't discoverable — also expose them via a button + dropdown-menu)
  • a button-triggered menu (use dropdown-menu)
  • selecting a form value (use select).

Overview

ContextMenu offers actions contextual to an element via right-click / long-press — canvas objects, file or list rows, rich editors. ContextMenuTrigger wraps the target area, ContextMenuContent opens at the pointer, and it carries the same item vocabulary as DropdownMenu (items, checkbox/radio items, labels, separators, submenus). It's discovered by gesture, so it complements a visible affordance — for a button-triggered menu use DropdownMenu.

Guidelines

Never make a context menu the only way to do something. Right-click/long-press is hard to discover and unavailable to many users — always provide a visible path (a kebab DropdownMenu, inline buttons) to the same actions. Use it to speed up expert workflows.

Scope items to the target and keep the list short. The menu should act on the element you opened it over; show only relevant actions, group them with separators, and keep destructive items distinct. Deep submenus get unwieldy — go one level where possible.

Support touch and keyboard. Long-press opens it on touch and it's fully arrow-key navigable once open; don't trap focus or rely on hover. Selecting an item closes the menu, so use checkbox/radio items for things that should persist it.

Best practices

Do
  • Mirror the same actions on a visible control — right-click alone isn't discoverable or fully accessible.
  • Group and separate destructive items; control checkbox/radio item state.
  • Give the trigger area real, meaningful content.
Don't
  • Don't make right-click the only way to reach an action.
  • Don't use it where users expect a button-triggered menu (dropdown-menu).
  • Don't nest deep submenu trees.

Content guidelines

  • Phrase items as actions, verb-first, and keep them short.
  • Mirror the labels of any equivalent button menu so the two stay consistent.

Troubleshooting

Keyboard or touch users can't reach the actions.

Cause. Right-click is the only opener.

Fix. Also expose the same actions via a visible control (e.g. a dropdown-menu); Radix supports Shift+F10/ContextMenu key on the focused trigger.

A checkbox item closes the menu when toggled, or its state resets.

Cause. Items close the menu on select by default, and item state must be lifted.

Fix. Call e.preventDefault() in onSelect to keep it open; control checked/onCheckedChange (or RadioGroup value).

Accessibility

Role
menu
ARIA APG
menu
Focus
Roving focus across items; focus returns to the trigger area on close.

Keyboard

Shift+F10ContextMenuOpen the menu from the focused trigger (keyboard).
ArrowUpArrowDownMove between items.
ArrowRightArrowLeftOpen / close a submenu.
EnterSpaceActivate the highlighted item.
EscapeClose the menu.
A1.3.1Info and RelationshipsA2.1.1KeyboardA4.1.2Name, Role, Value
  • Content is role=menu with roving focus; items are menuitem / menuitemcheckbox / menuitemradio.
  • Right-click / long-press is the primary opener — also expose the actions from a visible control for discoverability and accessibility.