Context Menu
React context menu with right-click actions, submenus, and keyboard navigation. Perfect for file managers, editors, and interactive interfaces.
Need right-click menus that feel native? Context menus appear exactly where users expect them - at their cursor position. Perfect for file operations, text editing, and any interface where users need quick actions without cluttering the main UI.
"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 with modern React patterns and full accessibility support.
npx shadcn@latest add context-menu
Why context menus work so well
Using the same patterns users know from their operating system:
- Right-click anywhere - Works exactly like desktop apps
- Cursor positioning - Menu appears right where you clicked
- Keyboard shortcuts - Arrow keys navigate, Enter selects, Escape closes
- Nested submenus - Handle complex action hierarchies naturally
- Screen reader friendly - Announces menu items and structure
- Mobile support - Long press works on touch devices
- Fast and lightweight - No performance impact when closed
Context menu patterns that users expect
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,}
API Reference
ContextMenu
Contains all the parts of a context menu. The root component.
Prop | Type | Default | Description |
---|---|---|---|
dir | "ltr" | "rtl" | - | The reading direction of the menu when applicable |
onOpenChange | (open: boolean) => void | - | Event handler called when the open state changes |
modal | boolean | true | Whether the menu is modal (traps focus and blocks interaction) |
ContextMenuTrigger
The area that opens the context menu. Wrap it around the target you want the context menu to open from.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
disabled | boolean | false | When true, the context menu won't open |
ContextMenuPortal
When used, portals the content part into the body.
Prop | Type | Default | Description |
---|---|---|---|
forceMount | boolean | false | Used to force mounting when more control is needed |
container | HTMLElement | document.body | Specify a container element to portal the content into |
ContextMenuContent
The component that pops out when the context menu is open.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
loop | boolean | false | Whether keyboard navigation should loop around |
onCloseAutoFocus | (event: Event) => void | - | Event handler called when focus moves to the trigger after closing |
onEscapeKeyDown | (event: KeyboardEvent) => void | - | Event handler called when the escape key is down |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void | - | Event handler called when a pointer event occurs outside |
onFocusOutside | (event: FocusOutsideEvent) => void | - | Event handler called when focus moves outside |
onInteractOutside | (event: InteractOutsideEvent) => void | - | Event handler called when an interaction happens outside |
forceMount | boolean | false | Used to force mounting when more control is needed |
alignOffset | number | 0 | The distance in pixels from the anchor |
avoidCollisions | boolean | true | Whether to avoid collisions with the boundary edges |
collisionBoundary | Element | null | Array<Element | null> | [] | The element used as the collision boundary |
collisionPadding | number | Partial<Record<Side, number>> | 0 | The distance in pixels from the boundary edges |
sticky | "partial" | "always" | "partial" | The sticky behavior on the align axis |
hideWhenDetached | boolean | false | Whether to hide when the reference element becomes fully occluded |
ContextMenuItem
The component that contains the context menu items.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
disabled | boolean | false | When true, prevents the user from interacting with the item |
onSelect | (event: Event) => void | - | Event handler called when the user selects an item |
textValue | string | - | Optional text used for typeahead purposes |
ContextMenuGroup
Used to group multiple ContextMenuItems.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
ContextMenuLabel
Used to render a label. It won't be focusable using arrow keys.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
ContextMenuCheckboxItem
An item that can be controlled and rendered like a checkbox.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
checked | boolean | "indeterminate" | - | The controlled checked state of the item |
onCheckedChange | (checked: boolean) => void | - | Event handler called when the checked state changes |
disabled | boolean | false | When true, prevents the user from interacting with the item |
onSelect | (event: Event) => void | - | Event handler called when the user selects an item |
textValue | string | - | Optional text used for typeahead purposes |
ContextMenuRadioGroup
Used to group multiple ContextMenuRadioItems.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
value | string | - | The controlled value of the radio item to check |
onValueChange | (value: string) => void | - | Event handler called when the value changes |
ContextMenuRadioItem
An item that can be controlled and rendered like a radio.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
value | string | - | The unique value of the item |
disabled | boolean | false | When true, prevents the user from interacting with the item |
onSelect | (event: Event) => void | - | Event handler called when the user selects an item |
textValue | string | - | Optional text used for typeahead purposes |
ContextMenuItemIndicator
Renders when the parent ContextMenuCheckboxItem or ContextMenuRadioItem is checked.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
forceMount | boolean | false | Used to force mounting when more control is needed |
ContextMenuSeparator
Used to visually separate items in the context menu.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
ContextMenuSub
Contains all the parts of a submenu.
Prop | Type | Default | Description |
---|---|---|---|
defaultOpen | boolean | false | The open state of the submenu when initially rendered |
open | boolean | - | The controlled open state of the submenu |
onOpenChange | (open: boolean) => void | - | Event handler called when the open state changes |
ContextMenuSubTrigger
An item that opens a submenu. Must be rendered inside ContextMenuSub.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
disabled | boolean | false | When true, prevents the user from interacting with the item |
textValue | string | - | Optional text used for typeahead purposes |
ContextMenuSubContent
The component that pops out when a submenu is open. Must be rendered inside ContextMenuSub.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Change the default rendered element for the one passed as a child |
loop | boolean | false | Whether keyboard navigation should loop around |
onEscapeKeyDown | (event: KeyboardEvent) => void | - | Event handler called when the escape key is down |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void | - | Event handler called when a pointer event occurs outside |
onFocusOutside | (event: FocusOutsideEvent) => void | - | Event handler called when focus moves outside |
onInteractOutside | (event: InteractOutsideEvent) => void | - | Event handler called when an interaction happens outside |
forceMount | boolean | false | Used to force mounting when more control is needed |
sideOffset | number | 0 | The distance in pixels from the trigger |
alignOffset | number | 0 | The distance in pixels from the anchor |
avoidCollisions | boolean | true | Whether to avoid collisions with the boundary edges |
collisionBoundary | Element | null | Array<Element | null> | [] | The element used as the collision boundary |
collisionPadding | number | Partial<Record<Side, number>> | 0 | The distance in pixels from the boundary edges |
arrowPadding | number | 0 | The padding between the arrow and the edges of the content |
sticky | "partial" | "always" | "partial" | The sticky behavior on the align axis |
hideWhenDetached | boolean | false | Whether to hide when the reference element becomes fully occluded |
ContextMenuShortcut
Used to display keyboard shortcuts. This is a custom component, not from Radix UI.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
children | React.ReactNode | - | The shortcut content to display |
Keyboard Navigation
Key | Description |
---|---|
Space | Activates the focused item |
Enter | Activates the focused item |
ArrowDown | Moves focus to the next item |
ArrowUp | Moves focus to the previous item |
ArrowRight | When focus is on ContextMenuSubTrigger, opens the submenu |
ArrowLeft | When focus is on ContextMenuSubTrigger, closes the submenu |
Escape | Closes the context menu and moves focus to the ContextMenuTrigger |
Context menu best practices
What makes context menus feel natural and useful:
- Use action verbs - "Edit" and "Delete" instead of "Edit this item" or "Delete selected"
- Group related actions - Use separators between different types of operations
- Put common actions first - Most-used items at the top save clicks
- Mark dangerous actions - Use red/destructive styling for things like "Delete"
- Show keyboard shortcuts - Power users appreciate seeing ⌘K next to actions
- Disable unavailable actions - Gray out options that don't apply right now
- Keep menus shallow - Deep nesting gets confusing fast
- Test on mobile - Long-press should work just like right-click
Command
React command palette with fuzzy search, keyboard shortcuts, and dialog support. Perfect for app navigation, search interfaces, and power user workflows.
Data Table
React data table with sorting, filtering, pagination, and selection. Built with TanStack Table for powerful data management and user interactions.