Displays a menu located at the pointer, triggered by a right click or a long press.
import * as React from "react";import { ContextMenu } from "radix-ui";import { DotFilledIcon, CheckIcon, ChevronRightIcon, } from "@radix-ui/react-icons";import "./styles.css";const ContextMenuDemo = () => {const [bookmarksChecked, setBookmarksChecked] = React.useState(true);const [urlsChecked, setUrlsChecked] = React.useState(false);const [person, setPerson] = React.useState("pedro");return (<ContextMenu.Root><ContextMenu.Trigger className="ContextMenuTrigger">Right-click here.</ContextMenu.Trigger><ContextMenu.Portal><ContextMenu.Content className="ContextMenuContent" sideOffset={5} align="end" ><ContextMenu.Item className="ContextMenuItem">Back <div className="RightSlot">⌘+[</div></ContextMenu.Item><ContextMenu.Item className="ContextMenuItem" disabled>Forward <div className="RightSlot">⌘+]</div></ContextMenu.Item><ContextMenu.Item className="ContextMenuItem">Reload <div className="RightSlot">⌘+R</div></ContextMenu.Item><ContextMenu.Sub><ContextMenu.SubTrigger className="ContextMenuSubTrigger">More Tools<div className="RightSlot"><ChevronRightIcon /></div></ContextMenu.SubTrigger><ContextMenu.Portal><ContextMenu.SubContent className="ContextMenuSubContent" sideOffset={2} alignOffset={-5} ><ContextMenu.Item className="ContextMenuItem">Save Page As… <div className="RightSlot">⌘+S</div></ContextMenu.Item><ContextMenu.Item className="ContextMenuItem">Create Shortcut…</ContextMenu.Item><ContextMenu.Item className="ContextMenuItem">Name Window…</ContextMenu.Item><ContextMenu.Separator className="ContextMenuSeparator" /><ContextMenu.Item className="ContextMenuItem">Developer Tools</ContextMenu.Item></ContextMenu.SubContent></ContextMenu.Portal></ContextMenu.Sub><ContextMenu.Separator className="ContextMenuSeparator" /><ContextMenu.CheckboxItem className="ContextMenuCheckboxItem" checked={bookmarksChecked} onCheckedChange={setBookmarksChecked} ><ContextMenu.ItemIndicator className="ContextMenuItemIndicator"><CheckIcon /></ContextMenu.ItemIndicator>Show Bookmarks <div className="RightSlot">⌘+B</div></ContextMenu.CheckboxItem><ContextMenu.CheckboxItem className="ContextMenuCheckboxItem" checked={urlsChecked} onCheckedChange={setUrlsChecked} ><ContextMenu.ItemIndicator className="ContextMenuItemIndicator"><CheckIcon /></ContextMenu.ItemIndicator>Show Full URLs</ContextMenu.CheckboxItem><ContextMenu.Separator className="ContextMenuSeparator" /><ContextMenu.Label className="ContextMenuLabel">People</ContextMenu.Label><ContextMenu.RadioGroup value={person} onValueChange={setPerson}><ContextMenu.RadioItem className="ContextMenuRadioItem" value="pedro" ><ContextMenu.ItemIndicator className="ContextMenuItemIndicator"><DotFilledIcon /></ContextMenu.ItemIndicator>Pedro Duarte</ContextMenu.RadioItem><ContextMenu.RadioItem className="ContextMenuRadioItem" value="colm" ><ContextMenu.ItemIndicator className="ContextMenuItemIndicator"><DotFilledIcon /></ContextMenu.ItemIndicator>Colm Tuite</ContextMenu.RadioItem></ContextMenu.RadioGroup></ContextMenu.Content></ContextMenu.Portal></ContextMenu.Root>);};export default ContextMenuDemo;
Supports submenus with configurable reading direction.
Supports items, labels, groups of items.
Supports checkable items (single or multiple) with optional indeterminate state.
Supports modal and non-modal modes.
Customize side, alignment, offsets, collision handling.
Focus is fully managed.
Full keyboard navigation.
Typeahead support.
Dismissing and layering behavior is highly customizable.
Triggers with a long press on touch devices
Install the component from your command line.
npm install @radix-ui/react-context-menu
Import all parts and piece them together.
import { ContextMenu } from "radix-ui";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger />
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Label />
<ContextMenu.Item />
<ContextMenu.Group>
<ContextMenu.Item />
</ContextMenu.Group>
<ContextMenu.CheckboxItem>
<ContextMenu.ItemIndicator />
</ContextMenu.CheckboxItem>
<ContextMenu.RadioGroup>
<ContextMenu.RadioItem>
<ContextMenu.ItemIndicator />
</ContextMenu.RadioItem>
</ContextMenu.RadioGroup>
<ContextMenu.Sub>
<ContextMenu.SubTrigger />
<ContextMenu.Portal>
<ContextMenu.SubContent />
</ContextMenu.Portal>
</ContextMenu.Sub>
<ContextMenu.Separator />
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
Adheres to the Menu WAI-ARIA design pattern and uses roving tabindex to manage focus movement among menu items.
Contains all the parts of a context menu.
Prop | Type | Default |
---|---|---|
dir | enum | |
onOpenChange | function | |
modal | boolean | true |
The area that opens the context menu. Wrap it around the target you want the context menu to open from when right-clicking (or using the relevant keyboard shortcuts).
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
disabled | boolean | false |
Data attribute | Values |
---|---|
[data-state] | "open" | "closed" |
When used, portals the content part into the body
.
Prop | Type | Default |
---|---|---|
forceMount | boolean | |
container | HTMLElement | document.body |
The component that pops out in an open context menu.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
loop | boolean | false |
onCloseAutoFocus | function | |
onEscapeKeyDown | function | |
onPointerDownOutside | function | |
onFocusOutside | function | |
onInteractOutside | function | |
forceMount | boolean | |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
Data attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-context-menu-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
--radix-context-menu-content-available-width | The remaining width between the trigger and the boundary edge |
--radix-context-menu-content-available-height | The remaining height between the trigger and the boundary edge |
--radix-context-menu-trigger-width | The width of the trigger |
--radix-context-menu-trigger-height | The height of the trigger |
An optional arrow element to render alongside a submenu. This can be used to help visually link the trigger item with the ContextMenu.Content
. Must be rendered inside ContextMenu.Content
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
width | number | 10 |
height | number | 5 |
The component that contains the context menu items.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
disabled | boolean | |
onSelect | function | |
textValue | string |
Data attribute | Values |
---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Used to group multiple ContextMenu.Item
s.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Used to render a label. It won't be focusable using arrow keys.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
An item that can be controlled and rendered like a checkbox.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
checked | boolean | 'indeterminate' | |
onCheckedChange | function | |
disabled | boolean | |
onSelect | function | |
textValue | string |
Data attribute | Values |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Used to group multiple ContextMenu.RadioItem
s.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
value | string | |
onValueChange | function |
An item that can be controlled and rendered like a radio.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
value* | string | |
disabled | boolean | |
onSelect | function | |
textValue | string |
Data attribute | Values |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Renders when the parent ContextMenu.CheckboxItem
or ContextMenu.RadioItem
is checked. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
forceMount | boolean |
Data attribute | Values |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
Used to visually separate items in the context menu.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Contains all the parts of a submenu.
Prop | Type | Default |
---|---|---|
defaultOpen | boolean | |
open | boolean | |
onOpenChange | function |
An item that opens a submenu. Must be rendered inside ContextMenu.Sub
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
disabled | boolean | |
textValue | string |
Data attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
The component that pops out when a submenu is open. Must be rendered inside ContextMenu.Sub
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
loop | boolean | false |
onEscapeKeyDown | function | |
onPointerDownOutside | function | |
onFocusOutside | function | |
onInteractOutside | function | |
forceMount | boolean | |
sideOffset | number | 0 |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
Data attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-context-menu-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
--radix-context-menu-content-available-width | The remaining width between the trigger and the boundary edge |
--radix-context-menu-content-available-height | The remaining height between the trigger and the boundary edge |
--radix-context-menu-trigger-width | The width of the trigger |
--radix-context-menu-trigger-height | The height of the trigger |
You can create submenus by using ContextMenu.Sub
in combination with its parts.
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Sub>
<ContextMenu.SubTrigger>Sub menu →</ContextMenu.SubTrigger>
<ContextMenu.Portal>
<ContextMenu.SubContent>
<ContextMenu.Item>Sub menu item</ContextMenu.Item>
<ContextMenu.Item>Sub menu item</ContextMenu.Item>
<ContextMenu.Arrow />
</ContextMenu.SubContent>
</ContextMenu.Portal>
</ContextMenu.Sub>
<ContextMenu.Separator />
<ContextMenu.Item>…</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
You can add special styles to disabled items via the data-disabled
attribute.
// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item className="ContextMenuItem" disabled>
…
</ContextMenu.Item>
<ContextMenu.Item className="ContextMenuItem">…</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuItem[data-disabled] {
color: gainsboro;
}
Use the Separator
part to add a separator between items.
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item>…</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
Use the Label
part to help label a section.
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Label>Label</ContextMenu.Label>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Item>…</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
Use the CheckboxItem
part to add an item that can be checked.
import * as React from "react";
import { CheckIcon } from "@radix-ui/react-icons";
import { ContextMenu } from "radix-ui";
export default () => {
const [checked, setChecked] = React.useState(true);
return (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Item>…</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.CheckboxItem
checked={checked}
onCheckedChange={setChecked}
>
<ContextMenu.ItemIndicator>
<CheckIcon />
</ContextMenu.ItemIndicator>
Checkbox item
</ContextMenu.CheckboxItem>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
};
Use the RadioGroup
and RadioItem
parts to add an item that can be checked amongst others.
import * as React from "react";
import { CheckIcon } from "@radix-ui/react-icons";
import { ContextMenu } from "radix-ui";
export default () => {
const [color, setColor] = React.useState("blue");
return (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.RadioGroup value={color} onValueChange={setColor}>
<ContextMenu.RadioItem value="red">
<ContextMenu.ItemIndicator>
<CheckIcon />
</ContextMenu.ItemIndicator>
Red
</ContextMenu.RadioItem>
<ContextMenu.RadioItem value="blue">
<ContextMenu.ItemIndicator>
<CheckIcon />
</ContextMenu.ItemIndicator>
Blue
</ContextMenu.RadioItem>
<ContextMenu.RadioItem value="green">
<ContextMenu.ItemIndicator>
<CheckIcon />
</ContextMenu.ItemIndicator>
Green
</ContextMenu.RadioItem>
</ContextMenu.RadioGroup>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
};
You can add extra decorative elements in the Item
parts, such as images.
import { ContextMenu } from "radix-ui";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item>
<img src="…" />
Adolfo Hess
</ContextMenu.Item>
<ContextMenu.Item>
<img src="…" />
Miyah Myles
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
You may want to constrain the width of the content (or sub-content) so that it matches the trigger (or sub-trigger) width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --radix-context-menu-trigger-width
and --radix-context-menu-content-available-height
to support this. Use them to constrain the content dimensions.
// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent">
…
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuContent {
width: var(--radix-context-menu-trigger-width);
max-height: var(--radix-context-menu-content-available-height);
}
We expose a CSS custom property --radix-context-menu-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent">
…
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuContent {
transform-origin: var(--radix-context-menu-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger>…</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent">
…
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.ContextMenuContent[data-side="top"] {
animation-name: slideUp;
}
.ContextMenuContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Uses roving tabindex to manage focus movement among menu items.
Key | Description |
---|---|
Space | Activates the focused item. |
Enter | Activates the focused item. |
ArrowDown | Moves focus to the next item. |
ArrowUp | Moves focus to the previous item. |
ArrowRightArrowLeft | When focus is on ContextMenu.SubTrigger , opens or closes the submenu depending on reading direction. |
Esc | Closes the context menu |