Context Menu
Right-click triggered menu. Positioned at the cursor; closes on outside click, scroll, or Escape. Content is portaled to document.body so it escapes any transformed/clipped ancestor.
Features
Opens at the exact cursor position on right-click.
Portaled to body so it escapes transformed ancestors.
Closes on outside click, Escape, or scroll outside the menu.
Disabled items and separators.
Blocks page scroll while open (no layout shift).
Example
Right-click here
Anatomy
import { ContextMenu } from '@wire-ui/react'
<ContextMenu.Root>
<ContextMenu.Trigger>Right-click here</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item onSelect={() => console.log('cut')}>Cut</ContextMenu.Item>
<ContextMenu.Item onSelect={() => console.log('copy')}>Copy</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item disabled>Paste</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>Styling
Root and Trigger expose data-state ("open" / "closed"). Item exposes the full set of interactive data attributes plus data-disabled.
<ContextMenu.Item className="
data-[hover]:bg-[#f5f5f5]
data-[focus-visible]:bg-[#f5f5f5]
data-[disabled]:opacity-40
data-[disabled]:cursor-not-allowed
">
Item
</ContextMenu.Item>Using data attributes
[data-state="open"] { /* trigger highlight while menu is open */ }
[data-disabled] { opacity: 0.4; cursor: not-allowed; }
[data-hover] { background: #f5f5f5; }
[data-focus-visible] { background: #f5f5f5; }Root props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Initial open state |
onOpenChange | (open: boolean) => void | — | Called when open state changes |
disabled | boolean | false | Disables the trigger (no right-click handling) |
Item props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Disables this item |
onSelect | () => void | — | Called when the item is selected (also closes the menu) |
Data attributes
| Attribute | Element | Values |
|---|---|---|
data-state | Root, Trigger, Content | "open" / "closed" |
data-disabled | Item | Present when disabled |
data-hover / data-active / data-focus-visible | Item | Standard interactive states |
Content sets role="menu". Items set role="menuitem" with aria-disabled when disabled. Separator sets role="separator".
Accessibility
- The
contextmenuevent is intercepted on the Trigger; the native browser menu is suppressed. - Content is portaled to
document.bodyand usesposition: fixed, so transformed ancestors don’t affect placement. - Items are keyboard-activatable with Enter or Space.
- Page scroll (wheel, touch, arrow keys) is blocked while the menu is open — no layout shift since the scrollbar stays in place.
Keyboard Interactions
| Key | Description |
|---|---|
| Enter | Activates the focused item. |
| Space | Activates the focused item. |
| Tab | Moves focus to the next item. |
| Escape | Closes the menu. |
Internals
Since0.3.0, the outside-click and Escape handling is delegated to useEventListener — null-target safe and no manual addEventListener / cleanup boilerplate. No public API change.
Last updated on