---
id: "packages/active-ui/context-menu"
title: "ContextMenu"
description: "A menu of actions on an object, opened with a right click, a long press or the context-menu key."
url: "/packages/active-ui/context-menu"
locale: "en"
revision: "65dfdaaa7d1b6b83"
---

# ContextMenu

A context menu offers actions on the thing under the pointer: a file, a row, a card. It opens with
a right click, a long press on a touch screen, or the keyboard's context-menu key. It offers
shortcuts to actions, never the only way to them: many readers never right-click. Its parts and
items are those of the [dropdown menu](/packages/active-ui/dropdown-menu).

**Example: Actions on a file**

Client: Runs entirely in your browser. Source: `examples/ui/context-menu.tsx`.

```tsx
// apps/docs/examples/ui/context-menu.tsx
'use client';

import {useState} from 'react';
import {FileTextIcon} from 'lucide-react';

import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from '@workspace/active-ui/components/primitives/context-menu';

/**
 * Actions on an object, opened with a right click, a long press on a touch
 * screen, or the context-menu key. It repeats actions that are also reachable
 * elsewhere: a context menu alone is hidden from readers who do not know it
 * is there.
 */
export default function ContextMenuExample() {
  const [last, setLast] = useState('nothing yet');
  const [pinned, setPinned] = useState(false);
  return (
    <div className="flex w-full max-w-sm flex-col items-center gap-4">
      <ContextMenu>
        <ContextMenuTrigger className="border-border text-muted-foreground flex h-36 w-full flex-col items-center justify-center gap-2 rounded-md border border-dashed text-sm">
          <FileTextIcon aria-hidden className="size-6" />
          Statement 2026-Q3.pdf
          <span className="text-xs">Right-click or long-press</span>
        </ContextMenuTrigger>
        <ContextMenuContent className="w-52">
          <ContextMenuGroup>
            <ContextMenuLabel>Statement 2026-Q3.pdf</ContextMenuLabel>
            <ContextMenuItem onClick={() => setLast('Open')}>
              Open
            </ContextMenuItem>
            <ContextMenuItem onClick={() => setLast('Download')}>
              Download
            </ContextMenuItem>
          </ContextMenuGroup>
          <ContextMenuCheckboxItem checked={pinned} onCheckedChange={setPinned}>
            Pin to the top
          </ContextMenuCheckboxItem>
          <ContextMenuSeparator />
          <ContextMenuItem
            variant="destructive"
            onClick={() => setLast('Delete')}
          >
            Delete
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
      <p className="text-muted-foreground text-sm" aria-live="polite">
        Last action: {last} · {pinned ? 'pinned' : 'not pinned'}
      </p>
    </div>
  );
}
```

## Usage

```tsx
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from '@workspace/active-ui/components/primitives/context-menu';

<ContextMenu>
  <ContextMenuTrigger>Statement 2026-Q3.pdf</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem onClick={open}>Open</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>;
```

## Anatomy

- `ContextMenu` and `ContextMenuTrigger`: the area that opens the menu. The menu opens at the
  pointer.
- `ContextMenuContent` with `ContextMenuItem` (`variant`, `inset`), `ContextMenuCheckboxItem`,
  `ContextMenuRadioGroup` with `ContextMenuRadioItem`, `ContextMenuGroup` with `ContextMenuLabel`,
  `ContextMenuSub` with its trigger and content, `ContextMenuSeparator` and `ContextMenuShortcut`:
  as in the dropdown menu.

## What your application provides

- The same actions somewhere visible, such as a button with a [dropdown menu](/packages/active-ui/dropdown-menu)
  on the object.
- What each item does.

## Accessibility and keyboard

- The trigger area is not a tab stop of its own. When it, or an element inside it, has focus, the
  context-menu key or `Shift+F10` opens the menu.
- Inside the menu, the keys of the dropdown menu apply; `Escape` closes it.

## API

### ContextMenuItem

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | no | Indents the text to line up with items that have an icon or indicator. |
| `variant` | `"default" \| "destructive"` | no | `destructive` colours an item that deletes or cannot be undone. |
| `closeOnClick` | `boolean` | no | Whether to close the menu when the item is clicked. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `id` | `string` | no |  |
| `label` | `string` | no | Overrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button. |
| `onClick` | `((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)` | no | The click handler for the menu item. |
| `render` | `ComponentRenderFn<HTMLProps, MenuItemState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

### Other parts

**ContextMenu**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `actionsRef` | `RefObject<MenuRootActions \| null>` | no | A ref to imperative actions. - `unmount`: Ends the closing phase of the menu after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the menu completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the menu imperatively when called. |
| `children` | `ReactNode` | no |  |
| `closeParentOnEsc` | `boolean` | no | **Deprecated.** This prop has no effect on Context Menu. |
| `defaultOpen` | `boolean` | no | Whether the menu is initially open.  To render a controlled menu, use the `open` prop instead. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `highlightItemOnHover` | `boolean` | no | Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. |
| `loopFocus` | `boolean` | no | Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. |
| `onItemHighlighted` | `((highlightedItem: HTMLElement \| undefined, eventDetails: MenuRoot.HighlightEventDetails) => void)` | no | Callback fired when an item is highlighted or unhighlighted. Receives the highlighted item element (or `undefined` if no item is highlighted) and details containing the reason for the change, the event, and the item's text label. The `reason` can be: - `'keyboard'`: the highlight changed due to keyboard navigation. - `'pointer'`: the highlight changed due to pointer hovering. The event may be a `MouseEvent` rather than a `PointerEvent`. - `'none'`: the highlight changed for another reason, such as automatic highlighting while filtering, the item list changing, or the popup opening or closing. |
| `onOpenChange` | `((open: boolean, eventDetails: ContextMenuRoot.ChangeEventDetails) => void)` | no | Event handler called when the menu is opened or closed. |
| `onOpenChangeComplete` | `((open: boolean) => void)` | no | Event handler called after any animations complete when the menu is opened or closed. |
| `open` | `boolean` | no | Whether the menu is currently open. |
| `orientation` | `MenuRootOrientation` | no | The visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. |

**ContextMenuTrigger**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, ContextMenuTriggerState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuContent**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `align` | `Align` | no | How to align the popup relative to the specified side. |
| `alignOffset` | `number \| OffsetFunction` | no | Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment.  The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side.  Defaults to `2` for root context menus when `side` is not specified and `align` is not `'center'`. Otherwise, it defaults to `0`. |
| `children` | `ReactNode` | no |  |
| `finalFocus` | `boolean \| RefObject<HTMLElement \| null> \| ((closeType: InteractionType) => boolean \| HTMLElement \| null \| void)` | no | Determines the element to focus when the menu is closed.  - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing. |
| `id` | `string` | no |  |
| `render` | `ComponentRenderFn<HTMLProps, MenuPopupState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `side` | `Side` | no | Which side of the anchor element to align the popup against. May automatically change to avoid collisions.  Submenus default to `'inline-end'`. |
| `sideOffset` | `number \| OffsetFunction` | no | Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment.  The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side.  Defaults to `-5` for root context menus when `side` is not specified and `align` is not `'center'`. Otherwise, it defaults to `0`. |

**ContextMenuCheckboxItem**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | no | Indents the text to line up with items that have an icon or indicator. |
| `checked` | `boolean` | no | Whether the checkbox item is currently ticked.  To render an uncontrolled checkbox item, use the `defaultChecked` prop instead. |
| `closeOnClick` | `boolean` | no | Whether to close the menu when the item is clicked. |
| `defaultChecked` | `boolean` | no | Whether the checkbox item is initially ticked.  To render a controlled checkbox item, use the `checked` prop instead. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `id` | `string` | no |  |
| `label` | `string` | no | Overrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button. |
| `onCheckedChange` | `((checked: boolean, eventDetails: MenuCheckboxItem.ChangeEventDetails) => void)` | no | Event handler called when the checkbox item is ticked or unticked. |
| `onClick` | `((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)` | no | The click handler for the menu item. |
| `render` | `ComponentRenderFn<HTMLProps, MenuCheckboxItemState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuRadioItem**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | no | Indents the text to line up with items that have an icon or indicator. |
| `value` | `any` | yes | Value of the radio item. This is the value that will be set in the MenuRadioGroup when the item is selected. |
| `closeOnClick` | `boolean` | no | Whether to close the menu when the item is clicked. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `id` | `string` | no |  |
| `label` | `string` | no | Overrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button. |
| `onClick` | `((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)` | no | The click handler for the menu item. |
| `render` | `ComponentRenderFn<HTMLProps, MenuRadioItemState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuLabel**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | no | Indents the text to line up with items that have an icon or indicator. |
| `render` | `ComponentRenderFn<HTMLProps, MenuGroupLabelState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuSeparator**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `orientation` | `Orientation` | no | The orientation of the separator. |
| `render` | `ComponentRenderFn<HTMLProps, SeparatorState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuShortcut**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

ContextMenuShortcut has no props of its own.

**ContextMenuGroup**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | no | The content of the component. |
| `render` | `ComponentRenderFn<HTMLProps, MenuGroupState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuPortal**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `container` | `HTMLElement \| RefObject<HTMLElement \| ShadowRoot \| null> \| ShadowRoot \| null` | no | A parent element to render the portal element into. |
| `keepMounted` | `boolean` | no | Whether to keep the portal mounted in the DOM while the popup is hidden. |
| `render` | `ComponentRenderFn<HTMLProps, MenuPortalState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuSub**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `actionsRef` | `RefObject<MenuRootActions \| null>` | no | A ref to imperative actions. - `unmount`: Ends the closing phase of the menu after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the menu completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the menu imperatively when called. |
| `children` | `ReactNode` | no | The content of the submenu. |
| `closeParentOnEsc` | `boolean` | no | When in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu. |
| `defaultOpen` | `boolean` | no | Whether the menu is initially open.  To render a controlled menu, use the `open` prop instead. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `highlightItemOnHover` | `boolean` | no | Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. |
| `loopFocus` | `boolean` | no | Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. |
| `onItemHighlighted` | `((highlightedItem: HTMLElement \| undefined, eventDetails: MenuRoot.HighlightEventDetails) => void)` | no | Callback fired when an item is highlighted or unhighlighted. Receives the highlighted item element (or `undefined` if no item is highlighted) and details containing the reason for the change, the event, and the item's text label. The `reason` can be: - `'keyboard'`: the highlight changed due to keyboard navigation. - `'pointer'`: the highlight changed due to pointer hovering. The event may be a `MouseEvent` rather than a `PointerEvent`. - `'none'`: the highlight changed for another reason, such as automatic highlighting while filtering, the item list changing, or the popup opening or closing. |
| `onOpenChange` | `((open: boolean, eventDetails: MenuSubmenuRoot.ChangeEventDetails) => void)` | no | Event handler called when the menu is opened or closed. |
| `onOpenChangeComplete` | `((open: boolean) => void)` | no | Event handler called after any animations complete when the menu is opened or closed. |
| `open` | `boolean` | no | Whether the menu is currently open. |
| `orientation` | `MenuRootOrientation` | no | The visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. |

**ContextMenuSubContent**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `align` | `Align` | no | How to align the popup relative to the specified side. |
| `alignOffset` | `number \| OffsetFunction` | no | Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment.  The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side.  Defaults to `2` for root context menus when `side` is not specified and `align` is not `'center'`. Otherwise, it defaults to `0`. |
| `children` | `ReactNode` | no |  |
| `finalFocus` | `boolean \| RefObject<HTMLElement \| null> \| ((closeType: InteractionType) => boolean \| HTMLElement \| null \| void)` | no | Determines the element to focus when the menu is closed.  - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing. |
| `id` | `string` | no |  |
| `render` | `ComponentRenderFn<HTMLProps, MenuPopupState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `side` | `Side` | no | Which side of the anchor element to align the popup against. May automatically change to avoid collisions.  Submenus default to `'inline-end'`. |
| `sideOffset` | `number \| OffsetFunction` | no | Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment.  The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side.  Defaults to `-5` for root context menus when `side` is not specified and `align` is not `'center'`. Otherwise, it defaults to `0`. |

**ContextMenuSubTrigger**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `inset` | `boolean` | no | Indents the text to line up with items that have an icon or indicator. |
| `closeDelay` | `number` | no | How long to wait before closing the menu that was opened on hover. Specified in milliseconds.  Requires the `openOnHover` prop. |
| `delay` | `number` | no | How long to wait before the menu may be opened on hover. Specified in milliseconds.  Requires the `openOnHover` prop. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `id` | `string` | no |  |
| `label` | `string` | no | Overrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button. |
| `onClick` | `((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)` | no |  |
| `openOnHover` | `boolean` | no | Whether the menu should also open when the trigger is hovered. |
| `render` | `ComponentRenderFn<HTMLProps, MenuSubmenuTriggerState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ContextMenuRadioGroup**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | no | The content of the component. |
| `defaultValue` | `any` | no | The uncontrolled value of the radio item that should be initially selected.  To render a controlled radio group, use the `value` prop instead. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `onValueChange` | `((value: any, eventDetails: MenuRadioGroup.ChangeEventDetails) => void)` | no | Function called when the selected value changes. |
| `render` | `ComponentRenderFn<HTMLProps, MenuRadioGroupState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `value` | `any` | no | The controlled value of the radio item that should be currently selected.  To render an uncontrolled radio group, use the `defaultValue` prop instead. |

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Context Menu documentation of shadcn/ui (MIT licence) to
> this package; see [Third-party notices](/third-party-notices).
