Experience System
Components

Context Menu

A menu opened by right-click or long-press, for actions and settings in context.

Right click here

Installation

The component is exported from @by/experience-system. Add the package with your package manager:

pnpm add @by/experience-system

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.

Composition

Use the following composition to build a ContextMenu:

ContextMenu
├── ContextMenuTrigger
└── ContextMenuContent
    ├── ContextMenuLabel (optional)
    ├── ContextMenuItem / CheckboxItem / RadioGroup / …
    ├── ContextMenuSeparator (optional)
    └── ContextMenuSub (optional)
        ├── ContextMenuSubTrigger
        └── ContextMenuSubContent

ContextMenuTrigger wraps the surface that opens the menu on pointer secondary click (right-click) or the platform equivalent. Put commands and groups inside ContextMenuContent. Use ContextMenuSub for nested menus. Behavior follows Radix UI Context Menu.

Usage

import {
  ContextMenu,
  ContextMenuTrigger,
  ContextMenuContent,
  ContextMenuItem,
} from '@by/experience-system';

ContextMenu and its parts are client components ('use client'). Use them inside a Client Component or a dynamic import when using the Next.js App Router.

<ContextMenu>
  <ContextMenuTrigger className="rounded-md border border-dashed px-4 py-8">Right click</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>Action</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

Pay attention:

Apply rtl:scale-x-[1] to icons that have an obvious "forward" direction, such as "ArrowUndo" and "ArrowRedo".

When to use

  • When you want to offer quick access to relevant actions based on the user's current selection or focus.
  • When space is limited and secondary actions don't need to be always visible.

When not to use

  • When the actions are critical or primary and need to be discoverable without extra clicks.
  • When the number of contextual options becomes too large, leading to clutter or overwhelm.

Examples

The live preview at the top of this page uses context-menu-usage (minimal trigger + one item).

Complex content

Items with leading icons, inset alignment for icon-less rows, shortcuts, a nested submenu (including an error-styled row via color="error"), checkbox items, and a radio group with an inset label.

Right click here

API Reference

Subsection titles name @by/experience-system exports and the Radix part they wrap. Pieces map to Radix UI Context Menu primitives. Styling, density-aware spacing, and icons are provided by @by/experience-system and @by/icons. Adheres to the Menu WAI-ARIA design pattern and uses roving tabindex to move focus among items.

ContextMenu

Root. ContextMenu contains all the parts of a context menu. Exported as ContextMenu; forwards ContextMenu.Root props from radix-ui.

PropTypeDefault
direnumNo default value
onOpenChangefunctionNo default value
modalbooleantrue

ContextMenuTrigger

ContextMenuTrigger — the area that opens the context menu. Wrap it around the target you want the menu to open from when right-clicking (or using the relevant keyboard shortcuts).

PropTypeDefault
asChildbooleanfalse
disabledbooleanfalse
Data attributeValues
[data-state]"open" | "closed"

ContextMenuPortal

ContextMenuPortal — when used, portals the content part into the document. In this experience system, container defaults to the element from useContainerElement() (ThemeProvider) instead of document.body.

PropTypeDefault
forceMountbooleanNo default value
containerHTMLElementFrom ThemeProvider (see above)

ContextMenuContent

ContextMenuContent — the component that pops out in an open context menu. This export wraps Portal with the Experience System container so you typically render ContextMenuContent directly (without a separate ContextMenuPortal for the main menu).

PropTypeDefault
asChildbooleanfalse
loopbooleanfalse
onCloseAutoFocusfunctionNo default value
onEscapeKeyDownfunctionNo default value
onPointerDownOutsidefunctionNo default value
onFocusOutsidefunctionNo default value
onInteractOutsidefunctionNo default value
forceMountbooleanNo default value
alignOffsetnumber0
avoidCollisionsbooleantrue
collisionBoundaryBoundary[]
collisionPaddingnumber | Padding0
stickyenum"partial"
hideWhenDetachedbooleanfalse
Data attributeValues
[data-state]"open" | "closed"
[data-side]"left" | "right" | "bottom" | "top"
[data-align]"start" | "end" | "center"
CSS variableDescription
--radix-context-menu-content-transform-originThe transform-origin computed from the content and arrow positions/offsets
--radix-context-menu-content-available-widthThe remaining width between the trigger and the boundary edge
--radix-context-menu-content-available-heightThe remaining height between the trigger and the boundary edge
--radix-context-menu-trigger-widthThe width of the trigger
--radix-context-menu-trigger-heightThe height of the trigger

Arrow (optional)

An optional arrow element to render alongside a submenu. This can be used to help visually link the trigger item with ContextMenu.Content. Must be rendered inside ContextMenu.Content.

PropTypeDefault
asChildbooleanfalse
widthnumber10
heightnumber5

@by/experience-system does not export ContextMenuArrow; ContextMenuSubTrigger includes a trailing chevron instead. For Radix’s optional arrow API, see the upstream docs.

ContextMenuItem

ContextMenuItem — the component that contains the context menu items.

PropTypeDefault
asChildbooleanfalse
disabledbooleanNo default value
onSelectfunctionNo default value
textValuestringNo default value
insetbooleanNo default value (experience system: aligns text with icon rows)
colorenum"neutral" ("neutral" | "error" — use error for irreversible actions)
Data attributeValues
[data-highlighted]Present when highlighted
[data-disabled]Present when disabled
[data-color]"neutral" | "error"

ContextMenuGroup

ContextMenuGroup — used to group multiple items.

PropTypeDefault
asChildbooleanfalse

ContextMenuLabel

ContextMenuLabel — used to render a label. It will not be focusable using arrow keys.

PropTypeDefault
asChildbooleanfalse
insetbooleanNo default value (experience system)

ContextMenuCheckboxItem

ContextMenuCheckboxItem — an item that can be controlled and rendered like a checkbox. Check rendering uses ContextMenuPrimitive.ItemIndicator internally with a Check icon.

PropTypeDefault
asChildbooleanfalse
checkedboolean | 'indeterminate'No default value
onCheckedChangefunctionNo default value
disabledbooleanNo default value
onSelectfunctionNo default value
textValuestringNo default value
Data attributeValues
[data-state]"checked" | "unchecked" | "indeterminate"
[data-highlighted]Present when highlighted
[data-disabled]Present when disabled

ContextMenuRadioGroup

ContextMenuRadioGroup — used to group multiple ContextMenuRadioItem components.

PropTypeDefault
asChildbooleanfalse
valuestringNo default value
onValueChangefunctionNo default value

ContextMenuRadioItem

ContextMenuRadioItem — An item that can be controlled and rendered like a radio. The indicator is built in (ShapeBulletCircleFill).

PropTypeDefault
asChildbooleanfalse
valuestringNo default value (required)
disabledbooleanNo default value
onSelectfunctionNo default value
textValuestringNo default value
Data attributeValues
[data-state]"checked" | "unchecked" | "indeterminate"
[data-highlighted]Present when highlighted
[data-disabled]Present when disabled

ItemIndicator

Renders when the parent CheckboxItem or RadioItem is checked. In @by/experience-system, indicators are internal to ContextMenuCheckboxItem and ContextMenuRadioItem (not a separate export).

PropTypeDefault
asChildbooleanfalse
forceMountbooleanNo default value
Data attributeValues
[data-state]"checked" | "unchecked" | "indeterminate"

ContextMenuSeparator

ContextMenuSeparator — used to visually separate items in the context menu.

PropTypeDefault
asChildbooleanfalse

ContextMenuShortcut

ContextMenuShortcut — Design-system helper: a span for keyboard hints, aligned to the trailing edge when placed inside an item. Not a Radix primitive part.

ContextMenuSub

ContextMenuSub — contains all the parts of a submenu.

PropTypeDefault
defaultOpenbooleanNo default value
openbooleanNo default value
onOpenChangefunctionNo default value

ContextMenuSubTrigger

ContextMenuSubTrigger — an item that opens a submenu. Must be rendered inside ContextMenuSub. Includes a trailing ArrowChevronRight icon.

PropTypeDefault
asChildbooleanfalse
disabledbooleanNo default value
textValuestringNo default value
insetbooleanNo default value (experience system)
Data attributeValues
[data-state]"open" | "closed"
[data-highlighted]Present when highlighted
[data-disabled]Present when disabled

ContextMenuSubContent

ContextMenuSubContent — the component that pops out when a submenu is open. Must be rendered inside ContextMenuSub.

PropTypeDefault
asChildbooleanfalse
loopbooleanfalse
onEscapeKeyDownfunctionNo default value
onPointerDownOutsidefunctionNo default value
onFocusOutsidefunctionNo default value
onInteractOutsidefunctionNo default value
forceMountbooleanNo default value
sideOffsetnumber0
alignOffsetnumber0
avoidCollisionsbooleantrue
collisionBoundaryBoundary[]
collisionPaddingnumber | Padding0
arrowPaddingnumber0
stickyenum"partial"
hideWhenDetachedbooleanfalse
Data attributeValues
[data-state]"open" | "closed"
[data-side]"left" | "right" | "bottom" | "top"
[data-align]"start" | "end" | "center"
CSS variableDescription
--radix-context-menu-content-transform-originThe transform-origin computed from the content and arrow positions/offsets
--radix-context-menu-content-available-widthThe remaining width between the trigger and the boundary edge
--radix-context-menu-content-available-heightThe remaining height between the trigger and the boundary edge
--radix-context-menu-trigger-widthThe width of the trigger
--radix-context-menu-trigger-heightThe height of the trigger

Accessibility

Uses roving tabindex to manage focus among menu items. Use ContextMenuLabel (optionally inset) to title sections when needed, and avoid relying only on shortcut text for essential information.

Keyboard interactions

KeyDescription
SpaceActivates the focused item.
EnterActivates the focused item.
ArrowDownMoves focus to the next item.
ArrowUpMoves focus to the previous item.
ArrowRight / ArrowLeftWhen focus is on ContextMenuSubTrigger, opens or closes the submenu depending on reading direction.
EscCloses the context menu.

For the full primitive reference and any upstream updates, see Radix UI — Context Menu.

Implementation: packages/experience-system/src/components/ContextMenu/ContextMenu.tsx. Codegen-oriented rules for AI agents: packages/experience-system/src/components/ContextMenu/ContextMenu_instructions.md.