Headless / Menu

ContextMenu

ContextMenu is a Menu summoned by a right-click. It opens at the pointer's coordinates and shares Menu's keyboard model exactly — only the trigger differs.

Loading playground…
Updated 3 days agoEdit on GitHubWeb & native

What it is

A compound component whose trigger is a region rather than a button. A right-click — the contextmenu event — opens the panel at the cursor. From there it behaves like Menu: role="menu", role="menuitem" rows, arrow-key navigation, Escape to close.

ContextMenu has no Trigger-as-button. The ContextMenu.Trigger wraps a region, and the menu's position comes from the click coordinates, not from a placement prop.

Install

example.tsx
yarn add @usemotif/headless
example.tsx
import { ContextMenu } from '@usemotif/headless';

Anatomy

example.tsx
<ContextMenu.Root>
<ContextMenu.Trigger>
  <Box>Right-click anywhere in here</Box>
</ContextMenu.Trigger>
<ContextMenu.Content>
  <ContextMenu.Item onSelect={cut}>Cut</ContextMenu.Item>
  <ContextMenu.Item onSelect={copy}>Copy</ContextMenu.Item>
  <ContextMenu.Item onSelect={paste}>Paste</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>

API

ContextMenu.Root

ContextMenu.Root

stable
function ContextMenu.Root(props: ContextMenuRootProps): JSX.Element
childrenReactNode

The Trigger and Content parts. ContextMenu.Root is uncontrolled — the open state is driven by the right-click.

ContextMenu.Trigger

ContextMenu.Trigger

stable
function ContextMenu.Trigger(props: ContextMenuTriggerProps): JSX.Element
childrenReactElementrequired

A single element — the region that listens for the right-click. ContextMenu clones it to attach the `onContextMenu` handler.

ContextMenu.Content

ContextMenu.Content

stable
function ContextMenu.Content(props: ContextMenuContentProps): JSX.Element | null
styleCSSProperties

Inline style for the panel — the pointer-derived position is applied on top.

childrenReactNode

The Item parts. ContextMenu also re-exports Menu.Separator.

ContextMenu.Item

Same shape as Menu.ItemonSelect, disabled, style, children. Activation closes the menu.

Accessibility

ContextMenu carries the same ARIA as Menu — role="menu", role="menuitem", aria-disabled on disabled rows. Opening the menu focuses the first enabled item; the arrow keys, Home, End, and Escape all behave as they do in Menu.

A right-click is a pointer-only gesture. Whatever a ContextMenu offers must also be reachable another way — a visible Menu button, a toolbar, a keyboard shortcut — so keyboard and touch users are not locked out of the actions.

Examples

A right-click menu on a file row:

example.tsx
<ContextMenu.Root>
<ContextMenu.Trigger>
  <Box p="$2" borderRadius="$radii.sm">{file.name}</Box>
</ContextMenu.Trigger>
<ContextMenu.Content style={{ padding: 4, background: 'var(--colors-surface-base)' }}>
  <ContextMenu.Item onSelect={() => rename(file)}>Rename</ContextMenu.Item>
  <ContextMenu.Item onSelect={() => download(file)}>Download</ContextMenu.Item>
  <ContextMenu.Item onSelect={() => remove(file)}>Delete</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>