Context Menu
Displays a menu to the user triggered by right-click or long-press.
Preview
Right click here
import { useState } from "react";
import {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
} from "@/components/ui/ContextMenu";
export function ContextMenuDemo() {
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-gray-6 text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-18">
<ContextMenuItem>
Back
<ContextMenuShortcut>⌘[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem disabled>
Forward
<ContextMenuShortcut>⌘]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
Reload
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>
Save As...
<ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>Print...</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)
}Installation
Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces
pnpm dlx shadcn@latest add @aura/context-menuManual
Install the following dependencies:
pnpm install @radix-ui/react-icons radix-uiCopy and paste the class names utility into your utils/class-names.ts file.
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Copy and paste the ContextMenu component into your components/ui/ContextMenu.tsx file.
"use client";
/**
* @description Displays a menu to the user triggered by right-click or long-press.
*/
import * as React from "react";
import { ContextMenu as ContextMenuRadix } from "radix-ui";
import {
CheckIcon,
ChevronRightIcon,
DotFilledIcon,
} from "@radix-ui/react-icons";
import { cn } from "@/utils/class-names";
function ContextMenu({
...props
}: React.ComponentProps<typeof ContextMenuRadix.Root>) {
return <ContextMenuRadix.Root data-slot="context-menu" {...props} />;
}
function ContextMenuTrigger({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.Trigger>) {
return (
<ContextMenuRadix.Trigger
data-slot="context-menu-trigger"
className={cn(className)}
{...props}
/>
);
}
function ContextMenuGroup({
...props
}: React.ComponentProps<typeof ContextMenuRadix.Group>) {
return <ContextMenuRadix.Group data-slot="context-menu-group" {...props} />;
}
function ContextMenuPortal({
...props
}: React.ComponentProps<typeof ContextMenuRadix.Portal>) {
return <ContextMenuRadix.Portal data-slot="context-menu-portal" {...props} />;
}
function ContextMenuSub({
...props
}: React.ComponentProps<typeof ContextMenuRadix.Sub>) {
return <ContextMenuRadix.Sub data-slot="context-menu-sub" {...props} />;
}
function ContextMenuRadioGroup({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.RadioGroup>) {
return (
<ContextMenuRadix.RadioGroup
data-slot="context-menu-radio-group"
className={cn(className)}
{...props}
/>
);
}
function ContextMenuSubTrigger({
children,
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.SubTrigger>) {
return (
<ContextMenuRadix.SubTrigger
data-slot="context-menu-sub-trigger"
className={cn(
className,
"p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer"
)}
{...props}
>
{children}
<div className="absolute right-0.5 top-0 bottom-0 items-center flex justify-center">
<ChevronRightIcon />
</div>
</ContextMenuRadix.SubTrigger>
);
}
function ContextMenuSubContent({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.SubContent>) {
return (
<ContextMenuRadix.SubContent
data-slot="context-menu-sub-content"
className={cn(
className,
"bg-gray-1 border border-gray-a6 rounded-sm relative shadow-md"
)}
{...props}
/>
);
}
function ContextMenuContent({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.Content>) {
return (
<ContextMenuRadix.Portal>
<ContextMenuRadix.Content
data-slot="context-menu-content"
collisionPadding={8}
className={cn(
className,
"bg-gray-1 border border-gray-a6 rounded-sm relative shadow-md"
)}
{...props}
/>
</ContextMenuRadix.Portal>
);
}
function ContextMenuItem({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.Item> & {}) {
return (
<ContextMenuRadix.Item
data-slot="context-menu-item"
className={cn(
className,
"p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer justify-between"
)}
{...props}
/>
);
}
function ContextMenuCheckboxItem({
children,
checked,
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.CheckboxItem>) {
return (
<ContextMenuRadix.CheckboxItem
data-slot="context-menu-checkbox-item"
className={cn(
className,
"p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer"
)}
checked={checked}
{...props}
>
<span className="absolute left-0.5 top-0 bottom-0 items-center flex justify-center">
<ContextMenuRadix.ItemIndicator>
<CheckIcon className="text-accent-8" />
</ContextMenuRadix.ItemIndicator>
</span>
{children}
</ContextMenuRadix.CheckboxItem>
);
}
function ContextMenuRadioItem({
children,
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.RadioItem>) {
return (
<ContextMenuRadix.RadioItem
data-slot="context-menu-radio-item"
className={cn(
className,
"p-0.5 px-2 hover:bg-accent-3 flex relative cursor-pointer"
)}
{...props}
>
<span className="absolute left-0.5 top-0 bottom-0 items-center flex justify-center">
<ContextMenuRadix.ItemIndicator>
<DotFilledIcon className="text-accent-8" />
</ContextMenuRadix.ItemIndicator>
</span>
{children}
</ContextMenuRadix.RadioItem>
);
}
function ContextMenuLabel({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.Label>) {
return (
<ContextMenuRadix.Label
data-slot="context-menu-label"
className={cn(className, "p-0.5 px-2 text-gray-12")}
{...props}
/>
);
}
function ContextMenuSeparator({
className,
...props
}: React.ComponentProps<typeof ContextMenuRadix.Separator>) {
return (
<ContextMenuRadix.Separator
data-slot="context-menu-separator"
className={cn(className, "m-0.6 h-px bg-gray-a6")}
{...props}
/>
);
}
function ContextMenuShortcut({ ...props }: React.ComponentProps<"span">) {
return <span data-slot="context-menu-shortcut" {...props} />;
}
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuGroup,
ContextMenuPortal,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
};Usage
WithSubmenu
export const WithSubmenu = () => (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-gray-6 text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-18">
<ContextMenuItem>Back</ContextMenuItem>
<ContextMenuItem>Forward</ContextMenuItem>
<ContextMenuItem>Reload</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuSub>
<ContextMenuSubTrigger>More Tools</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-18">
<ContextMenuItem>
Save Page As...
<ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>Create Shortcut...</ContextMenuItem>
<ContextMenuItem>Name Window...</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>Developer Tools</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem>
Inspect
<ContextMenuShortcut>⌥⌘I</ContextMenuShortcut>
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)WithCheckboxes
export const WithCheckboxes = () => {
const [showBookmarksBar, setShowBookmarksBar] = useState(true);
const [showFullUrls, setShowFullUrls] = useState(false);
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-gray-6 text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-18">
<ContextMenuCheckboxItem
checked={showBookmarksBar}
onCheckedChange={setShowBookmarksBar}
>
Show Bookmarks Bar
<ContextMenuShortcut>⌘⇧B</ContextMenuShortcut>
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem
checked={showFullUrls}
onCheckedChange={setShowFullUrls}
>
Show Full URLs
</ContextMenuCheckboxItem>
</ContextMenuContent>
</ContextMenu>
);
};WithRadioGroup
export const WithRadioGroup = () => {
const [person, setPerson] = useState("pedro");
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-gray-6 text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-18">
<ContextMenuRadioGroup value={person} onValueChange={setPerson}>
<ContextMenuLabel>People</ContextMenuLabel>
<ContextMenuSeparator />
<ContextMenuRadioItem value="pedro">
Pedro Duarte
</ContextMenuRadioItem>
<ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenu>
);
};ComplexMenu
export const ComplexMenu = () => {
const [showBookmarksBar, setShowBookmarksBar] = useState(true);
const [showFullUrls, setShowFullUrls] = useState(false);
const [person, setPerson] = useState("pedro");
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-gray-6 text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-20">
<ContextMenuItem>
Back
<ContextMenuShortcut>⌘[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem disabled>
Forward
<ContextMenuShortcut>⌘]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
Reload
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuCheckboxItem
checked={showBookmarksBar}
onCheckedChange={setShowBookmarksBar}
>
Show Bookmarks Bar
<ContextMenuShortcut>⌘⇧B</ContextMenuShortcut>
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem
checked={showFullUrls}
onCheckedChange={setShowFullUrls}
>
Show Full URLs
</ContextMenuCheckboxItem>
<ContextMenuSeparator />
<ContextMenuRadioGroup value={person} onValueChange={setPerson}>
<ContextMenuLabel>People</ContextMenuLabel>
<ContextMenuSeparator />
<ContextMenuRadioItem value="pedro">
Pedro Duarte
</ContextMenuRadioItem>
<ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenu>
);
};