Add this component

npx zentauri-ui add context-menu

Accessibility notes

Overlay patterns include focus, dismissal, and semantic roles where the component owns interaction. Verify trigger labels and escape behavior in your flow.

Dependency notes

Check Installation for shared peers. This component keeps styling in Tailwind classes and the --zui-* token contract.

Overlay

Right-click actions for dense product surfaces

ContextMenu opens from the native contextmenu gesture and renders an action surface at the pointer position. Use it for file rows, data tables, canvases, schedulers, and operational dashboards where secondary actions should stay close to the object being edited.

Examples

ContextMenu gives dashboards and work surfaces a predictable right-click action model with labels, separators, disabled items, and nested actions.



Context Menu variants playground

Pick an item variant, spacing, and pattern to preview the context menu live. Toggle Show output / Show code and the snippet updates to match the selected variant.


Context Menu API

Generated from the package prop types and variant definitions.

ContextMenu

ContextMenuProps

State

PropTypeDefault
defaultOpenboolean | undefinednone
onOpenChange((open: boolean) => void) | undefinednone
openboolean | undefinednone

Behavior

PropTypeDefault
closeOnEscapeboolean | undefinednone
closeOnOutsideClickboolean | undefinednone

Content

PropTypeDefault
children*ReactNodenone

ContextMenuContent

ContextMenuContentProps

Variants

PropTypeDefault
spacing
defaultlgmdnonesmxl
default

Behavior

PropTypeDefault
collisionPaddingnumber | undefinednone
widthnumber | undefinednone

Content

PropTypeDefault
childrenReactNodenone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ContextMenuItem

ContextMenuItemProps

Variants

PropTypeDefault
variant
amberaquablackbluecrimsoncyandefaultelectricemeraldforestghostgradient-blue+35
default

Behavior

PropTypeDefault
closeOnSelectboolean | undefinednone
disabledboolean | undefinednone
insetboolean | undefinednone
leftIconReactNodenone
onSelect(ReactEventHandler<HTMLDivElement> & (() => void)) | undefinednone
rightIconReactNodenone

Content

PropTypeDefault
childrenReactNodenone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ContextMenuLabel

ContextMenuLabelProps

Behavior

PropTypeDefault
insetboolean | undefinednone

Content

PropTypeDefault
childrenReactNodenone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLParagraphElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ContextMenuSeparator

ContextMenuSeparatorProps

Inherited HTML props
PropTypeDefault
childrenReactNodenone
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ContextMenuSub

ContextMenuSubProps

State

PropTypeDefault
defaultOpenboolean | undefinednone

Content

PropTypeDefault
children*ReactNodenone

ContextMenuSubContent

ContextMenuSubContentProps

Behavior

PropTypeDefault
spacing'default' | 'md' | 'sm' | 'lg' | 'none' | 'xl' | null | undefinednone

Content

PropTypeDefault
childrenReactNodenone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ContextMenuSubTrigger

ContextMenuSubTriggerProps

Behavior

PropTypeDefault
disabledboolean | undefinednone
insetboolean | undefinednone
rightIconReactNodenone
variant'default' | 'outline' | 'ghost' | 'blue' | 'cyan' | 'green' | 'lime' | 'mint' | 'ocean' | 'sapphire' | 'lavender' | 'ruby' | 'red' | 'slate' | 'zinc' | 'stone' | 'royal' | 'electric' | 'forest' | 'sunset' | 'magenta' | 'crimson' | 'aqua' | 'plum' | 'sky' | 'rose' | 'purple' | 'pink' | 'orange' | 'yellow' | 'teal' | 'indigo' | 'emerald' | 'gradient-blue' | 'gradient-green' | 'gradient-red' | 'gradient-yellow' | 'gradient-purple' | 'gradient-teal' | 'gradient-indigo' | 'gradient-pink' | 'gradient-orange' | 'gray' | 'violet' | 'amber' | 'white' | 'black' | null | undefinednone

Content

PropTypeDefault
childrenReactNodenone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

ContextMenuTrigger

ContextMenuTriggerProps

Behavior

PropTypeDefault
disabledboolean | undefinednone

Content

PropTypeDefault
children*ReactNodenone
Inherited HTML props
PropTypeDefault
classNamestring | undefinednone

GetSafePosition

GetSafePositionProps

Behavior

PropTypeDefault
collisionPadding*numbernone
height*numbernone
position*ContextMenuPosition | nullnone
width*numbernone
CSS variable overrides

ContextMenu CSS variables

ContextMenu shares Dropdown content and item variables, plus label and separator tokens, so menu theming stays aligned across right-click and click-triggered actions.

386 variables

Pattern: --zui-<component>-<slot?>-<variant?>-<property>-<state?>-dark?

:root {
  --zui-dropdown-content-border: oklch(20.8% 0.042 265.755 / 0.1);
  --zui-dropdown-content-bg: oklch(96.8% 0.007 247.896);
  --zui-dropdown-content-fg: oklch(20.8% 0.042 265.755);
  --zui-dropdown-item-ring-focus: oklch(55.1% 0.027 264.364 / 0.6);
  --zui-dropdown-item-default-bg-hover: oklch(96.7% 0.003 264.542);
  --zui-dropdown-item-default-fg-hover: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-default-fg: oklch(21% 0.034 264.665);
  --zui-dropdown-item-outline-bg-hover: oklch(96.7% 0.003 264.542);
  --zui-dropdown-item-outline-fg-hover: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-outline-border: #000000;
  --zui-dropdown-item-outline-fg: oklch(21% 0.034 264.665);
  --zui-dropdown-item-ghost-bg-hover: oklch(96.7% 0.003 264.542);
  --zui-dropdown-item-ghost-fg-hover: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-ghost-bg: transparent;
  --zui-dropdown-item-ghost-fg: oklch(21% 0.034 264.665);
  --zui-dropdown-item-white-bg-hover: oklch(96.7% 0.003 264.542);
  --zui-dropdown-item-white-fg-hover: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-white-bg: #ffffff;
  --zui-dropdown-item-white-fg: oklch(21% 0.034 264.665);
  --zui-dropdown-item-black-bg-hover: oklch(96.7% 0.003 264.542);
  --zui-dropdown-item-black-fg-hover: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-black-bg: #000000;
  --zui-dropdown-item-black-fg: #ffffff;
  --zui-dropdown-item-sky-bg-hover: oklch(95.1% 0.026 236.824);
  --zui-dropdown-item-sky-fg-hover: oklch(44.3% 0.11 240.79);
  --zui-dropdown-item-sky-bg: oklch(90.1% 0.058 230.902);
  --zui-dropdown-item-sky-fg: oklch(39.1% 0.09 240.876);
  --zui-dropdown-item-rose-bg-hover: oklch(94.1% 0.03 12.58);
  --zui-dropdown-item-rose-fg-hover: oklch(45.5% 0.188 13.697);
  --zui-dropdown-item-rose-bg: oklch(89.2% 0.058 10.001);
  --zui-dropdown-item-rose-fg: oklch(41% 0.159 10.272);
  --zui-dropdown-item-purple-bg-hover: oklch(94.6% 0.033 307.174);
  --zui-dropdown-item-purple-fg-hover: oklch(43.8% 0.218 303.724);
  --zui-dropdown-item-purple-bg: oklch(90.2% 0.063 306.703);
  --zui-dropdown-item-purple-fg: oklch(38.1% 0.176 304.987);
  --zui-dropdown-item-pink-bg-hover: oklch(94.8% 0.028 342.258);
  --zui-dropdown-item-pink-fg-hover: oklch(45.9% 0.187 3.815);
  --zui-dropdown-item-pink-bg: oklch(89.9% 0.061 343.231);
  --zui-dropdown-item-pink-fg: oklch(40.8% 0.153 2.432);
  --zui-dropdown-item-orange-bg-hover: oklch(95.4% 0.038 75.164);
  --zui-dropdown-item-orange-fg-hover: oklch(47% 0.157 37.304);
  --zui-dropdown-item-orange-bg: oklch(90.1% 0.076 70.697);
  --zui-dropdown-item-orange-fg: oklch(40.8% 0.123 38.172);
  --zui-dropdown-item-yellow-bg-hover: oklch(97.3% 0.071 103.193);
  --zui-dropdown-item-yellow-fg-hover: oklch(47.6% 0.114 61.907);
  --zui-dropdown-item-yellow-bg: oklch(94.5% 0.129 101.54);
  --zui-dropdown-item-yellow-fg: oklch(42.1% 0.095 57.708);
  --zui-dropdown-item-teal-bg-hover: oklch(95.3% 0.051 180.801);
  --zui-dropdown-item-teal-fg-hover: oklch(43.7% 0.078 188.216);
  --zui-dropdown-item-teal-bg: oklch(91% 0.096 180.426);
  --zui-dropdown-item-teal-fg: oklch(38.6% 0.063 188.416);
  --zui-dropdown-item-indigo-bg-hover: oklch(93% 0.034 272.788);
  --zui-dropdown-item-indigo-fg-hover: oklch(39.8% 0.195 277.366);
  --zui-dropdown-item-indigo-bg: oklch(87% 0.065 274.039);
  --zui-dropdown-item-indigo-fg: oklch(35.9% 0.144 278.697);
  --zui-dropdown-item-emerald-bg-hover: oklch(95% 0.052 163.051);
  --zui-dropdown-item-emerald-fg-hover: oklch(43.2% 0.095 166.913);
  --zui-dropdown-item-emerald-bg: oklch(90.5% 0.093 164.15);
  --zui-dropdown-item-emerald-fg: oklch(37.8% 0.077 168.94);
  --zui-dropdown-item-gray-bg-hover: oklch(96.7% 0.003 264.542);
  --zui-dropdown-item-gray-fg-hover: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-gray-bg: oklch(92.8% 0.006 264.531);
  --zui-dropdown-item-gray-fg: oklch(55.1% 0.027 264.364);
  --zui-dropdown-item-amber-bg-hover: oklch(96.2% 0.059 95.617);
  --zui-dropdown-item-amber-fg-hover: oklch(47.3% 0.137 46.201);
  --zui-dropdown-item-amber-bg: oklch(92.4% 0.12 95.746);
  --zui-dropdown-item-amber-fg: oklch(41.4% 0.112 45.904);
  --zui-dropdown-item-violet-bg-hover: oklch(94.3% 0.029 294.588);
  --zui-dropdown-item-violet-fg-hover: oklch(43.2% 0.232 292.759);
  --zui-dropdown-item-violet-bg: oklch(89.4% 0.057 293.283);
  --zui-dropdown-item-violet-fg: oklch(38% 0.189 293.745);
  --zui-dropdown-item-gradient-blue-from: oklch(42.4% 0.199 265.638);
  --zui-dropdown-item-gradient-blue-to: oklch(43.8% 0.218 303.724);
  --zui-dropdown-item-gradient-blue-fg-hover: oklch(62.3% 0.214 259.815);
  --zui-dropdown-item-gradient-blue-fg: oklch(37.9% 0.146 265.522);
  --zui-dropdown-item-gradient-green-from: oklch(44.8% 0.119 151.328);
  --zui-dropdown-item-gradient-green-to: oklch(45.3% 0.124 130.933);
  --zui-dropdown-item-gradient-green-fg-hover: oklch(72.3% 0.219 149.579);
  --zui-dropdown-item-gradient-green-fg: oklch(39.3% 0.095 152.535);
  --zui-dropdown-item-gradient-red-from: oklch(44.4% 0.177 26.899);
  --zui-dropdown-item-gradient-red-to: oklch(45.9% 0.187 3.815);
  --zui-dropdown-item-gradient-red-fg-hover: oklch(63.7% 0.237 25.331);
  --zui-dropdown-item-gradient-red-fg: oklch(39.6% 0.141 25.723);
  --zui-dropdown-item-gradient-yellow-from: oklch(47.6% 0.114 61.907);
  --zui-dropdown-item-gradient-yellow-to: oklch(47% 0.157 37.304);
  --zui-dropdown-item-gradient-yellow-fg-hover: oklch(79.5% 0.184 86.047);
  --zui-dropdown-item-gradient-yellow-fg: oklch(42.1% 0.095 57.708);
  --zui-dropdown-item-gradient-purple-from: oklch(43.8% 0.218 303.724);
  --zui-dropdown-item-gradient-purple-to: oklch(45.9% 0.187 3.815);
  --zui-dropdown-item-gradient-purple-fg-hover: oklch(62.7% 0.265 303.9);
  --zui-dropdown-item-gradient-purple-fg: oklch(38.1% 0.176 304.987);
  --zui-dropdown-item-gradient-teal-from: oklch(43.7% 0.078 188.216);
  --zui-dropdown-item-gradient-teal-to: oklch(45% 0.085 224.283);
  --zui-dropdown-item-gradient-teal-fg-hover: oklch(70.4% 0.14 182.503);
  --zui-dropdown-item-gradient-teal-fg: oklch(38.6% 0.063 188.416);
  --zui-dropdown-item-gradient-indigo-from: oklch(39.8% 0.195 277.366);
  --zui-dropdown-item-gradient-indigo-to: oklch(43.8% 0.218 303.724);
  --zui-dropdown-item-gradient-indigo-fg-hover: oklch(58.5% 0.233 277.117);
  --zui-dropdown-item-gradient-indigo-fg: oklch(35.9% 0.144 278.697);
  --zui-dropdown-item-gradient-pink-from: oklch(45.9% 0.187 3.815);
  --zui-dropdown-item-gradient-pink-to: oklch(45.5% 0.188 13.697);
  --zui-dropdown-item-gradient-pink-fg-hover: oklch(65.6% 0.241 354.308);
  --zui-dropdown-item-gradient-pink-fg: oklch(40.8% 0.153 2.432);
  --zui-dropdown-item-gradient-orange-from: oklch(47% 0.157 37.304);
  --zui-dropdown-item-gradient-orange-to: oklch(44.4% 0.177 26.899);
  --zui-dropdown-item-gradient-orange-fg-hover: oklch(70.5% 0.213 47.604);
  --zui-dropdown-item-gradient-orange-fg: oklch(40.8% 0.123 38.172);
  --zui-dropdown-item-blue-bg-hover: #2563eb18;
  --zui-dropdown-item-blue-fg-hover: #2563eb;
  --zui-dropdown-item-blue-bg: #2563eb10;
  --zui-dropdown-item-blue-fg: #2563eb;
  --zui-dropdown-item-cyan-bg-hover: #0891b218;
  --zui-dropdown-item-cyan-fg-hover: #0891b2;
  --zui-dropdown-item-cyan-bg: #0891b210;
  --zui-dropdown-item-cyan-fg: #0891b2;
  --zui-dropdown-item-green-bg-hover: #16a34a18;
  --zui-dropdown-item-green-fg-hover: #16a34a;
  --zui-dropdown-item-green-bg: #16a34a10;
  --zui-dropdown-item-green-fg: #16a34a;
  --zui-dropdown-item-lime-bg-hover: #65a30d18;
  --zui-dropdown-item-lime-fg-hover: #65a30d;
  --zui-dropdown-item-lime-bg: #65a30d10;
  --zui-dropdown-item-lime-fg: #65a30d;
  --zui-dropdown-item-mint-bg-hover: #10b98118;
  --zui-dropdown-item-mint-fg-hover: #10b981;
  --zui-dropdown-item-mint-bg: #10b98110;
  --zui-dropdown-item-mint-fg: #10b981;
  --zui-dropdown-item-ocean-bg-hover: #0284c718;
  --zui-dropdown-item-ocean-fg-hover: #0284c7;
  --zui-dropdown-item-ocean-bg: #0284c710;
  --zui-dropdown-item-ocean-fg: #0284c7;
  --zui-dropdown-item-sapphire-bg-hover: #1d4ed818;
  --zui-dropdown-item-sapphire-fg-hover: #1d4ed8;
  --zui-dropdown-item-sapphire-bg: #1d4ed810;
  --zui-dropdown-item-sapphire-fg: #1d4ed8;
  --zui-dropdown-item-lavender-bg-hover: #8b5cf618;
  --zui-dropdown-item-lavender-fg-hover: #8b5cf6;
  --zui-dropdown-item-lavender-bg: #8b5cf610;
  --zui-dropdown-item-lavender-fg: #8b5cf6;
  --zui-dropdown-item-ruby-bg-hover: #be123c18;
  --zui-dropdown-item-ruby-fg-hover: #be123c;
  --zui-dropdown-item-ruby-bg: #be123c10;
  --zui-dropdown-item-ruby-fg: #be123c;
  --zui-dropdown-item-red-bg-hover: #dc262618;
  --zui-dropdown-item-red-fg-hover: #dc2626;
  --zui-dropdown-item-red-bg: #dc262610;
  --zui-dropdown-item-red-fg: #dc2626;
  --zui-dropdown-item-slate-bg-hover: #47556918;
  --zui-dropdown-item-slate-fg-hover: #475569;
  --zui-dropdown-item-slate-bg: #47556910;
  --zui-dropdown-item-slate-fg: #475569;
  --zui-dropdown-item-zinc-bg-hover: #52525b18;
  --zui-dropdown-item-zinc-fg-hover: #52525b;
  --zui-dropdown-item-zinc-bg: #52525b10;
  --zui-dropdown-item-zinc-fg: #52525b;
  --zui-dropdown-item-stone-bg-hover: #57534e18;
  --zui-dropdown-item-stone-fg-hover: #57534e;
  --zui-dropdown-item-stone-bg: #57534e10;
  --zui-dropdown-item-stone-fg: #57534e;
  --zui-dropdown-item-royal-bg-hover: #4338ca18;
  --zui-dropdown-item-royal-fg-hover: #4338ca;
  --zui-dropdown-item-royal-bg: #4338ca10;
  --zui-dropdown-item-royal-fg: #4338ca;
  --zui-dropdown-item-electric-bg-hover: #0ea5e918;
  --zui-dropdown-item-electric-fg-hover: #0ea5e9;
  --zui-dropdown-item-electric-bg: #0ea5e910;
  --zui-dropdown-item-electric-fg: #0ea5e9;
  --zui-dropdown-item-forest-bg-hover: #16653418;
  --zui-dropdown-item-forest-fg-hover: #166534;
  --zui-dropdown-item-forest-bg: #16653410;
  --zui-dropdown-item-forest-fg: #166534;
  --zui-dropdown-item-sunset-bg-hover: #ea580c18;
  --zui-dropdown-item-sunset-fg-hover: #ea580c;
  --zui-dropdown-item-sunset-bg: #ea580c10;
  --zui-dropdown-item-sunset-fg: #ea580c;
  --zui-dropdown-item-magenta-bg-hover: #c026d318;
  --zui-dropdown-item-magenta-fg-hover: #c026d3;
  --zui-dropdown-item-magenta-bg: #c026d310;
  --zui-dropdown-item-magenta-fg: #c026d3;
  --zui-dropdown-item-crimson-bg-hover: #b91c1c18;
  --zui-dropdown-item-crimson-fg-hover: #b91c1c;
  --zui-dropdown-item-crimson-bg: #b91c1c10;
  --zui-dropdown-item-crimson-fg: #b91c1c;
  --zui-dropdown-item-aqua-bg-hover: #0f766e18;
  --zui-dropdown-item-aqua-fg-hover: #0f766e;
  --zui-dropdown-item-aqua-bg: #0f766e10;
  --zui-dropdown-item-aqua-fg: #0f766e;
  --zui-dropdown-item-plum-bg-hover: #7e22ce18;
  --zui-dropdown-item-plum-fg-hover: #7e22ce;
  --zui-dropdown-item-plum-bg: #7e22ce10;
  --zui-dropdown-item-plum-fg: #7e22ce;
  --zui-dropdown-label-fg: oklch(55.1% 0.027 264.364);
  --zui-dropdown-separator-bg: oklch(20.8% 0.042 265.755 / 0.12);
}

/* Dark theme variables follow the same names with -dark appended. */
.dark {
  --zui-dropdown-content-border-dark: #ffffff1a;
  --zui-dropdown-content-bg-dark: oklch(20.8% 0.042 265.755);
}

What it does

ContextMenu turns the browser contextmenu event into a themed React menu. It positions content at the pointer, clamps the menu inside the viewport, and closes on outside click, Escape, or item selection.

Composition and API

Compose ContextMenu with ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuSeparator, and the ContextMenuSub parts. Items support the same variants as Dropdown items, and content supports the same spacing tokens.

Common use cases

  • Attach file actions such as open, rename, duplicate, move, and delete to explorer rows.
  • Give data tables compact row-level actions without adding another visible button column.
  • Group advanced workspace or dashboard operations behind nested submenus.
  • Use disabled menu items to explain unavailable actions without changing layout.

Accessibility

ContextMenuContent uses role=menu and items use role=menuitem. Items can be selected by pointer, Enter, or Space, Escape closes the menu and returns focus to the trigger, and disabled actions expose aria-disabled.

Next.js integration notes

ContextMenu is a client component because it listens to pointer and keyboard events. Keep the preview route itself server-rendered and pass SEO content into the interactive sections, matching the rest of the App Router preview pages.

FAQ

Does ContextMenu share Dropdown styling?

Yes. ContextMenu uses the same dropdown item variants and dropdown-scoped CSS variables, so existing menu theming carries across.

Can ContextMenu render nested actions?

Yes. Use ContextMenuSub, ContextMenuSubTrigger, and ContextMenuSubContent to create hover and keyboard-opened nested menus.

How should I label a ContextMenu?

Use ContextMenuLabel for short group labels and ContextMenuSeparator between action groups. The label renders as a paragraph element so it follows the preview code example labeling pattern.