Shadcn Context Menu
React context menu component for right-click actions and submenus. Built with TypeScript and Tailwind CSS for Next.js applications using Radix UI primitives.
Context menu positioning?
Join our Discord community for help from other developers.
Ever built an app where users kept asking "How do I right-click on this?" or watched them hunt through toolbars for actions they expected to find in a context menu? Yeah, missing right-click support makes web apps feel broken to desktop users. This shadcn/ui context menu brings native OS-style interactions to your React application.
Context menu showcase
Right-click interactions that feel familiar:
"use client"import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuDemo() { return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm"> Right click here </ContextMenuTrigger> <ContextMenuContent className="w-52"> <ContextMenuItem inset> Back <ContextMenuShortcut>⌘[</ContextMenuShortcut> </ContextMenuItem> <ContextMenuItem inset disabled> Forward <ContextMenuShortcut>⌘]</ContextMenuShortcut> </ContextMenuItem> <ContextMenuItem inset> Reload <ContextMenuShortcut>⌘R</ContextMenuShortcut> </ContextMenuItem> <ContextMenuSub> <ContextMenuSubTrigger inset>More Tools</ContextMenuSubTrigger> <ContextMenuSubContent className="w-44"> <ContextMenuItem>Save Page...</ContextMenuItem> <ContextMenuItem>Create Shortcut...</ContextMenuItem> <ContextMenuItem>Name Window...</ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem>Developer Tools</ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem variant="destructive">Delete</ContextMenuItem> </ContextMenuSubContent> </ContextMenuSub> <ContextMenuSeparator /> <ContextMenuCheckboxItem checked> Show Bookmarks </ContextMenuCheckboxItem> <ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem> <ContextMenuSeparator /> <ContextMenuRadioGroup value="pedro"> <ContextMenuLabel inset>People</ContextMenuLabel> <ContextMenuRadioItem value="pedro"> Pedro Duarte </ContextMenuRadioItem> <ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem> </ContextMenuRadioGroup> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Built on Radix UI primitives with full accessibility support. Styled with Tailwind CSS so it matches your design system instead of looking like a generic dropdown menu.
npx shadcn@latest add context-menu
Why context menus actually improve user experience
Here's the thing—context menus aren't just about adding features, they're about meeting user expectations. Desktop users have been right-clicking for decades. When your web app doesn't respond to right-clicks, it feels incomplete.
Think about how you use your computer. Right-click a file, get file operations. Right-click text, get editing options. Right-click in VS Code, get code actions. Users bring these muscle memories to your app. This free shadcn context menu component lets you honor those expectations in your React projects.
Context menus also solve the toolbar clutter problem. Instead of cramming every possible action into your interface, you put common actions where users can see them and secondary actions behind a right-click. Your Next.js application stays clean while power users get the tools they need in TypeScript-safe, accessible components. React developers love this pattern because it keeps interfaces minimal while maintaining functionality.
Common context menu patterns you'll actually use
File and folder operations
The classic right-click menu for managing items:
"use client"import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuSimple() { return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[100px] w-[200px] items-center justify-center rounded-md border text-sm"> Right click me </ContextMenuTrigger> <ContextMenuContent> <ContextMenuItem>Profile</ContextMenuItem> <ContextMenuItem>Billing</ContextMenuItem> <ContextMenuItem>Team</ContextMenuItem> <ContextMenuItem>Subscription</ContextMenuItem> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Actions with visual icons
Icons help users quickly identify common actions:
"use client"import { Copy, Edit, Share, Trash2, User, Settings, Download, Heart,} from "lucide-react"import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuWithIcons() { return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[150px] w-[200px] items-center justify-center rounded-lg border bg-muted/40 text-sm"> Right click for options </ContextMenuTrigger> <ContextMenuContent className="w-48"> <ContextMenuItem> <User className="mr-2 h-4 w-4" /> Profile </ContextMenuItem> <ContextMenuItem> <Settings className="mr-2 h-4 w-4" /> Settings </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem> <Copy className="mr-2 h-4 w-4" /> Copy </ContextMenuItem> <ContextMenuItem> <Share className="mr-2 h-4 w-4" /> Share </ContextMenuItem> <ContextMenuItem> <Download className="mr-2 h-4 w-4" /> Download </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem> <Heart className="mr-2 h-4 w-4" /> Add to favorites </ContextMenuItem> <ContextMenuItem> <Edit className="mr-2 h-4 w-4" /> Edit </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem variant="destructive"> <Trash2 className="mr-2 h-4 w-4" /> Delete </ContextMenuItem> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Toggle settings and preferences
Perfect for view options and application settings:
"use client"import * as React from "react"import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuLabel, ContextMenuSeparator, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuCheckboxes() { const [showBookmarksBar, setShowBookmarksBar] = React.useState(true) const [showFullUrls, setShowFullUrls] = React.useState(false) const [showDeveloperMenu, setShowDeveloperMenu] = React.useState(true) return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[150px] w-[250px] items-center justify-center rounded-md border text-sm"> Right click to toggle options </ContextMenuTrigger> <ContextMenuContent className="w-56"> <ContextMenuLabel>View Options</ContextMenuLabel> <ContextMenuSeparator /> <ContextMenuCheckboxItem checked={showBookmarksBar} onCheckedChange={setShowBookmarksBar} > Show Bookmarks Bar </ContextMenuCheckboxItem> <ContextMenuCheckboxItem checked={showFullUrls} onCheckedChange={setShowFullUrls} > Show Full URLs </ContextMenuCheckboxItem> <ContextMenuCheckboxItem checked={showDeveloperMenu} onCheckedChange={setShowDeveloperMenu} > Show Developer Menu </ContextMenuCheckboxItem> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Choose between options
Radio groups let users pick one choice from several:
"use client"import * as React from "react"import { ContextMenu, ContextMenuContent, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuRadioGroupDemo() { const [selectedTheme, setSelectedTheme] = React.useState("light") const [selectedSize, setSelectedSize] = React.useState("medium") return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[150px] w-[250px] items-center justify-center rounded-md border text-sm"> Right click to select theme </ContextMenuTrigger> <ContextMenuContent className="w-48"> <ContextMenuRadioGroup value={selectedTheme} onValueChange={setSelectedTheme}> <ContextMenuLabel>Theme</ContextMenuLabel> <ContextMenuSeparator /> <ContextMenuRadioItem value="light"> Light </ContextMenuRadioItem> <ContextMenuRadioItem value="dark"> Dark </ContextMenuRadioItem> <ContextMenuRadioItem value="system"> System </ContextMenuRadioItem> </ContextMenuRadioGroup> <ContextMenuSeparator /> <ContextMenuRadioGroup value={selectedSize} onValueChange={setSelectedSize}> <ContextMenuLabel>Text Size</ContextMenuLabel> <ContextMenuSeparator /> <ContextMenuRadioItem value="small"> Small </ContextMenuRadioItem> <ContextMenuRadioItem value="medium"> Medium </ContextMenuRadioItem> <ContextMenuRadioItem value="large"> Large </ContextMenuRadioItem> </ContextMenuRadioGroup> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Organize complex actions
Submenus keep related actions grouped without clutter:
"use client"import { ArrowRight, Copy, Edit, FileText, Folder, Image, Settings, Share, Trash2,} from "lucide-react"import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuSubmenu() { return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[150px] w-[250px] items-center justify-center rounded-md border text-sm"> Right click for file actions </ContextMenuTrigger> <ContextMenuContent className="w-52"> <ContextMenuItem> <Edit className="mr-2 h-4 w-4" /> Edit <ContextMenuShortcut>⌘E</ContextMenuShortcut> </ContextMenuItem> <ContextMenuItem> <Copy className="mr-2 h-4 w-4" /> Make a copy <ContextMenuShortcut>⌘C</ContextMenuShortcut> </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuSub> <ContextMenuSubTrigger> <Share className="mr-2 h-4 w-4" /> Share <ArrowRight className="ml-auto h-4 w-4" /> </ContextMenuSubTrigger> <ContextMenuSubContent className="w-48"> <ContextMenuItem> <FileText className="mr-2 h-4 w-4" /> Share as PDF </ContextMenuItem> <ContextMenuItem> <Image className="mr-2 h-4 w-4" /> Share as Image </ContextMenuItem> <ContextMenuItem> <Folder className="mr-2 h-4 w-4" /> Share to folder </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem>Email link</ContextMenuItem> <ContextMenuItem>Copy link</ContextMenuItem> </ContextMenuSubContent> </ContextMenuSub> <ContextMenuSub> <ContextMenuSubTrigger> <Settings className="mr-2 h-4 w-4" /> More actions <ArrowRight className="ml-auto h-4 w-4" /> </ContextMenuSubTrigger> <ContextMenuSubContent className="w-44"> <ContextMenuItem>Rename</ContextMenuItem> <ContextMenuItem>Move to folder</ContextMenuItem> <ContextMenuItem>Add to favorites</ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem>Properties</ContextMenuItem> </ContextMenuSubContent> </ContextMenuSub> <ContextMenuSeparator /> <ContextMenuItem variant="destructive"> <Trash2 className="mr-2 h-4 w-4" /> Delete <ContextMenuShortcut>⌘⌫</ContextMenuShortcut> </ContextMenuItem> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Handle restricted content
Show what's available and what isn't with disabled states:
"use client"import { Copy, Edit, Share, Trash2, Lock } from "lucide-react"import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuTrigger,} from "@/components/ui/context-menu"export default function ContextMenuDisabled() { return ( <div className="w-full p-6 flex justify-center"> <ContextMenu> <ContextMenuTrigger className="flex h-[150px] w-[250px] items-center justify-center rounded-md border text-sm"> Read-only document <br /> <span className="text-xs text-muted-foreground mt-2"> Right click (some actions disabled) </span> </ContextMenuTrigger> <ContextMenuContent className="w-48"> <ContextMenuItem> <Copy className="mr-2 h-4 w-4" /> Copy <ContextMenuShortcut>⌘C</ContextMenuShortcut> </ContextMenuItem> <ContextMenuItem disabled> <Edit className="mr-2 h-4 w-4" /> Edit <ContextMenuShortcut>⌘E</ContextMenuShortcut> </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem> <Share className="mr-2 h-4 w-4" /> Share </ContextMenuItem> <ContextMenuItem disabled> <Lock className="mr-2 h-4 w-4" /> Change permissions </ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem disabled variant="destructive"> <Trash2 className="mr-2 h-4 w-4" /> Delete <ContextMenuShortcut>⌘⌫</ContextMenuShortcut> </ContextMenuItem> </ContextMenuContent> </ContextMenu> </div> )}
"use client"import * as React from "react"import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"import { cn } from "@/lib/utils"function ContextMenu({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) { return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />}function ContextMenuTrigger({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { return ( <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} /> )}function ContextMenuGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { return ( <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> )}function ContextMenuPortal({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) { return ( <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} /> )}function ContextMenuSub({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) { return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />}function ContextMenuRadioGroup({ ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) { return ( <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} /> )}function ContextMenuSubTrigger({ className, inset, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean}) { return ( <ContextMenuPrimitive.SubTrigger data-slot="context-menu-sub-trigger" data-inset={inset} className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > {children} <ChevronRightIcon className="ml-auto" /> </ContextMenuPrimitive.SubTrigger> )}function ContextMenuSubContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { return ( <ContextMenuPrimitive.SubContent data-slot="context-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className )} {...props} /> )}function ContextMenuContent({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) { return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content data-slot="context-menu-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className )} {...props} /> </ContextMenuPrimitive.Portal> )}function ContextMenuItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { inset?: boolean variant?: "default" | "destructive"}) { return ( <ContextMenuPrimitive.Item data-slot="context-menu-item" data-inset={inset} data-variant={variant} className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function ContextMenuCheckboxItem({ className, children, checked, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) { return ( <ContextMenuPrimitive.CheckboxItem data-slot="context-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} checked={checked} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CheckIcon className="size-4" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.CheckboxItem> )}function ContextMenuRadioItem({ className, children, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) { return ( <ContextMenuPrimitive.RadioItem data-slot="context-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} > <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <ContextMenuPrimitive.ItemIndicator> <CircleIcon className="size-2 fill-current" /> </ContextMenuPrimitive.ItemIndicator> </span> {children} </ContextMenuPrimitive.RadioItem> )}function ContextMenuLabel({ className, inset, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean}) { return ( <ContextMenuPrimitive.Label data-slot="context-menu-label" data-inset={inset} className={cn( "text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className )} {...props} /> )}function ContextMenuSeparator({ className, ...props}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { return ( <ContextMenuPrimitive.Separator data-slot="context-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> )}function ContextMenuShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="context-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup,}
Features
This free open source context menu component includes everything you need:
- TypeScript-first - Full type safety with proper event handlers and menu state
- Radix UI powered - Battle-tested accessibility and keyboard navigation
- Cursor positioning - Appears exactly where users right-click, like native apps
- Tailwind CSS styled - Customize with utilities, not fighting component CSS
- Mobile optimized - Long-press works on touch devices automatically
- Submenu support - Nest actions without creating navigation nightmares
- Keyboard accessible - Arrow keys, Enter, Escape work exactly as expected
- Portal rendering - Avoids z-index conflicts by rendering in document body
API Reference
Core Components
Component | Purpose | Key Props |
---|---|---|
ContextMenu | Root container | dir , onOpenChange , modal |
ContextMenuTrigger | Right-click target | asChild , disabled |
ContextMenuContent | Menu container | loop , alignOffset , avoidCollisions |
ContextMenuItem | Individual action | disabled , onSelect |
Interactive Elements
Component | Purpose | Key Props |
---|---|---|
ContextMenuCheckboxItem | Toggle option | checked , onCheckedChange , disabled |
ContextMenuRadioGroup | Single choice group | value , onValueChange |
ContextMenuRadioItem | Radio option | value , disabled |
ContextMenuSub | Nested submenu | defaultOpen , open , onOpenChange |
Keyboard Navigation
Key | Action |
---|---|
Space | Activates the focused item |
Enter | Activates the focused item |
ArrowDown | Moves focus to the next item |
ArrowUp | Moves focus to the previous item |
ArrowRight | Opens submenu when focused on trigger |
ArrowLeft | Closes submenu when focused on trigger |
Escape | Closes the context menu |
Production tips
Make right-click discoverable. This free shadcn/ui context menu works perfectly, but users need to know right-clicking does something. Add subtle hints like "Right-click for options" or show a small menu icon next to items. Your React component handles the functionality—you handle the discoverability that makes it useful in Next.js applications.
Group actions logically. Don't dump 15 random actions in one menu. Group by action type—File operations, Edit actions, View options. Use separators between groups. This TypeScript component provides the structure—you provide the organization that actually helps users. React applications benefit from logical grouping because it matches how users think about tasks.
Put dangerous actions last. Delete, Remove, and other destructive actions should be at the bottom of menus, preferably with visual warnings. Users scan menus top to bottom. Your JavaScript context menu should protect against accidental clicks, not encourage them.
Test with real content. Those neat demos with "Copy" and "Paste" work great until you have "Export Selected Items to PDF with Custom Template Settings". Long action names break layouts. This open source shadcn component handles the UI—you handle the content that actually reflects production usage.
Mobile needs special consideration. Long-press to open context menus works, but touch targets need to be finger-friendly. Consider showing context actions in different ways on small screens—maybe as bottom sheets or action buttons instead of tiny dropdown menus.
Integration with other components
Context menus naturally extend your existing shadcn components in React applications. Use Button components within menu items for complex actions that need additional styling or loading states.
For rich menu displays, combine with Avatar components to show user actions or Badge components for showing status indicators next to menu options. This open source pattern works well for team collaboration interfaces.
When building content management systems, pair context menus with Dialog components for confirmation actions. Your JavaScript context menu triggers the confirmation—the dialog handles the actual destructive operation safely.
For form-heavy applications, context menus can trigger Sheet workflows for editing content. Separator components help organize complex menu structures into logical action groups that make sense to users.
Questions you might have
Shadcn Command
React command palette component with fast search and keyboard shortcuts. Built with TypeScript and Tailwind CSS for Next.js applications using cmdk library.
Shadcn Data Table
React data table component with sorting, filtering, pagination, and row selection. Built with TypeScript and Tailwind CSS for Next.js using TanStack Table.