import { CopyIcon, FolderIcon, ScissorsIcon, Trash2Icon } from "lucide-react";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
export default function ContextMenuBasic() {
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-40 w-full max-w-sm flex-col items-center justify-center gap-1 rounded-md border border-dashed border-border-strong text-center text-sm text-muted-foreground">
<span>Right-click here</span>
<span className="text-xs text-faint">or long-press on touch devices</span>
</ContextMenuTrigger>
<ContextMenuContent className="w-56">
<ContextMenuItem>
<FolderIcon />
Open
<ContextMenuShortcut>⌘O</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<ScissorsIcon />
Cut
<ContextMenuShortcut>⌘X</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<CopyIcon />
Copy
<ContextMenuShortcut>⌘C</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem variant="destructive">
<Trash2Icon />
Move to trash
<ContextMenuShortcut>⌘⌫</ContextMenuShortcut>
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}- Add the registryOnce per project
pnpm dlx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json - Add context-menu
pnpm dlx shadcn@latest add @jarvis/context-menu
Usage
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
export function FileContextMenu() {
return (
<ContextMenu>
<ContextMenuTrigger className="rounded-md border p-6">homepage-v2.fig</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>
Open
<ContextMenuShortcut>⌘O</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>Rename</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem variant="destructive">Move to trash</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}ContextMenuTrigger wraps the area that opens the menu. Items work like those in a
Dropdown Menu.
People won't always think to right-click, so make these actions available somewhere else too.
Composition
Examples
Submenus
Nest a menu with ContextMenuSub, ContextMenuSubTrigger and ContextMenuSubContent.
import { FileTextIcon, FolderIcon, ImageIcon, PlusIcon } from "lucide-react";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
export default function ContextMenuSubmenu() {
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-40 w-full max-w-sm items-center justify-center rounded-md border border-dashed border-border-strong text-sm text-muted-foreground">
Right-click here
</ContextMenuTrigger>
<ContextMenuContent className="w-56">
<ContextMenuSub>
<ContextMenuSubTrigger>
<PlusIcon />
New
</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-44">
<ContextMenuItem>
<FolderIcon />
Folder
</ContextMenuItem>
<ContextMenuItem>
<FileTextIcon />
Document
</ContextMenuItem>
<ContextMenuItem>
<ImageIcon />
Image
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuItem>Refresh</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}Checkbox and radio items
Use ContextMenuCheckboxItem for toggles, and ContextMenuRadioItems inside a
ContextMenuRadioGroup to pick one of several options.
import * as React from "react";
import {
ContextMenu,
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
export default function ContextMenuCheckboxAndRadioItems() {
const [showHidden, setShowHidden] = React.useState(false);
const [showPreview, setShowPreview] = React.useState(true);
const [sort, setSort] = React.useState("name");
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-40 w-full max-w-sm items-center justify-center rounded-md border border-dashed border-border-strong text-sm text-muted-foreground">
Right-click here
</ContextMenuTrigger>
<ContextMenuContent className="w-56">
<ContextMenuCheckboxItem checked={showHidden} onCheckedChange={setShowHidden}>
Show hidden files
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem checked={showPreview} onCheckedChange={setShowPreview}>
Show preview
</ContextMenuCheckboxItem>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuLabel>Sort by</ContextMenuLabel>
<ContextMenuRadioGroup value={sort} onValueChange={setSort}>
<ContextMenuRadioItem value="name">Name</ContextMenuRadioItem>
<ContextMenuRadioItem value="modified">Date modified</ContextMenuRadioItem>
<ContextMenuRadioItem value="size">Size</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuGroup>
</ContextMenuContent>
</ContextMenu>
);
}Props
Items, labels and submenu triggers take inset to line up with items that have an icon.
The tables leave out className, style and render. Parts built on Base UI take them as its styling and composition guides describe.
ContextMenu
| Prop | Type | Default |
|---|---|---|
onOpenChangeEvent handler called when the menu is opened or closed. | ((open: boolean, eventDetails: ContextMenu.Root.ChangeEventDetails) => void) | — |
closeParentOnEsc | boolean | — |
children | React.ReactNode | — |
defaultOpenWhether the menu is initially open. To render a controlled menu, use the | boolean | false |
loopFocusWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | boolean | true |
highlightItemOnHoverWhether moving the pointer over items should highlight them.
Disabling this prop allows CSS | boolean | true |
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed. | ((open: boolean) => void) | — |
onItemHighlightedCallback fired when an item is highlighted or unhighlighted.
Receives the highlighted item element (or
| ((highlightedItem: HTMLElement | undefined, eventDetails: MenuRoot.HighlightEventDetails) => void) | — |
openWhether the menu is currently open. | boolean | — |
orientationThe visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. | "horizontal" | "vertical" | 'vertical' |
disabledWhether the component should ignore user interaction. | boolean | false |
actionsRefA ref to imperative actions.
| React.RefObject<MenuRootActions | null> | — |
ContextMenuPortal
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | React.RefObject<HTMLElement | ShadowRoot | null> | ShadowRoot | null | — |
ContextMenuContent
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
id | string | — |
finalFocusDetermines the element to focus when the menu is closed.
| boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | HTMLElement | null | void) | — |
sideWhich side of the anchor element to align the popup against. May automatically change to avoid collisions. Submenus default to | "bottom" | "inline-end" | "inline-start" | "left" | "right" | "top" | "right" |
sideOffsetDistance 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
Defaults to | number | OffsetFunction | 0 |
alignHow to align the popup relative to the specified side. | "center" | "end" | "start" | "start" |
alignOffsetAdditional 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
Defaults to | number | OffsetFunction | 4 |
ContextMenuGroup
| Prop | Type | Default |
|---|---|---|
childrenThe content of the component. | React.ReactNode | — |
ContextMenuLabel
| Prop | Type | Default |
|---|---|---|
inset | boolean | — |
ContextMenuItem
| Prop | Type | Default |
|---|---|---|
onClickThe click handler for the menu item. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
labelOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | string | — |
id | string | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | true |
nativeButtonWhether the component renders a native | boolean | false |
inset | boolean | — |
variant | "default" | "destructive" | "default" |
ContextMenuSub
| Prop | Type | Default |
|---|---|---|
onOpenChangeEvent handler called when the menu is opened or closed. | ((open: boolean, eventDetails: ContextMenu.SubmenuRoot.ChangeEventDetails) => void) | — |
closeParentOnEscWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu. | boolean | false |
childrenThe content of the submenu. | React.ReactNode | — |
defaultOpenWhether the menu is initially open. To render a controlled menu, use the | boolean | false |
loopFocusWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | boolean | true |
highlightItemOnHoverWhether moving the pointer over items should highlight them.
Disabling this prop allows CSS | boolean | true |
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed. | ((open: boolean) => void) | — |
onItemHighlightedCallback fired when an item is highlighted or unhighlighted.
Receives the highlighted item element (or
| ((highlightedItem: HTMLElement | undefined, eventDetails: MenuRoot.HighlightEventDetails) => void) | — |
openWhether the menu is currently open. | boolean | — |
orientationThe visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. | "horizontal" | "vertical" | 'vertical' |
disabledWhether the component should ignore user interaction. | boolean | false |
actionsRefA ref to imperative actions.
| React.RefObject<MenuRootActions | null> | — |
ContextMenuSubTrigger
| Prop | Type | Default |
|---|---|---|
onClick | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
labelOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | string | — |
id | string | — |
disabledWhether the component should ignore user interaction. | boolean | false |
delayHow long to wait before the menu may be opened on hover. Specified in milliseconds. Requires the | number | 100 |
closeDelayHow long to wait before closing the menu that was opened on hover. Specified in milliseconds. Requires the | number | 0 |
openOnHoverWhether the menu should also open when the trigger is hovered. | boolean | true |
nativeButtonWhether the component renders a native | boolean | false |
inset | boolean | — |
ContextMenuSubContent
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
id | string | — |
finalFocusDetermines the element to focus when the menu is closed.
| boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | HTMLElement | null | void) | — |
sideWhich side of the anchor element to align the popup against. May automatically change to avoid collisions. Submenus default to | "bottom" | "inline-end" | "inline-start" | "left" | "right" | "top" | "right" |
sideOffsetDistance 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
Defaults to | number | OffsetFunction | 2 |
alignHow to align the popup relative to the specified side. | "center" | "end" | "start" | "start" |
alignOffsetAdditional 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
Defaults to | number | OffsetFunction | -2 |
ContextMenuCheckboxItem
| Prop | Type | Default |
|---|---|---|
checkedWhether the checkbox item is currently ticked. To render an uncontrolled checkbox item, use the | boolean | — |
defaultCheckedWhether the checkbox item is initially ticked. To render a controlled checkbox item, use the | boolean | false |
onCheckedChangeEvent handler called when the checkbox item is ticked or unticked. | ((checked: boolean, eventDetails: ContextMenu.CheckboxItem.ChangeEventDetails) => void) | — |
onClickThe click handler for the menu item. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
labelOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | string | — |
id | string | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | false |
nativeButtonWhether the component renders a native | boolean | false |
inset | boolean | — |
ContextMenuRadioGroup
| Prop | Type | Default |
|---|---|---|
childrenThe content of the component. | React.ReactNode | — |
valueThe controlled value of the radio item that should be currently selected. To render an uncontrolled radio group, use the | any | — |
defaultValueThe uncontrolled value of the radio item that should be initially selected. To render a controlled radio group, use the | any | — |
onValueChangeFunction called when the selected value changes. | ((value: any, eventDetails: ContextMenu.RadioGroup.ChangeEventDetails) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
ContextMenuRadioItem
| Prop | Type | Default |
|---|---|---|
valueValue of the radio item. This is the value that will be set in the MenuRadioGroup when the item is selected. | any | — |
onClickThe click handler for the menu item. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
labelOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | string | — |
id | string | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | false |
nativeButtonWhether the component renders a native | boolean | false |
inset | boolean | — |
ContextMenuSeparator
| Prop | Type | Default |
|---|---|---|
orientationThe orientation of the separator. | "horizontal" | "vertical" | 'horizontal' |