Displays a menu to the user — such as a set of actions or functions — triggered by a button.
import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuGroupLabel, ContextMenuItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, } from "@repo/tailwindcss/ui/context-menu"; const ContextMenuDemo = () => { return ( <ContextMenu> <ContextMenuTrigger class="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm"> Right click here </ContextMenuTrigger> <ContextMenuContent class="w-64"> <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 class="w-48"> <ContextMenuItem> Save Page As... <ContextMenuShortcut>⇧⌘S</ContextMenuShortcut> </ContextMenuItem> <ContextMenuItem>Create Shortcut...</ContextMenuItem> <ContextMenuItem>Name Window...</ContextMenuItem> <ContextMenuSeparator /> <ContextMenuItem>Developer Tools</ContextMenuItem> </ContextMenuSubContent> </ContextMenuSub> <ContextMenuSeparator /> <ContextMenuCheckboxItem checked> Show Bookmarks Bar <ContextMenuShortcut>⌘⇧B</ContextMenuShortcut> </ContextMenuCheckboxItem> <ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem> <ContextMenuSeparator /> <ContextMenuGroup> <ContextMenuGroupLabel inset>People</ContextMenuGroupLabel> <ContextMenuSeparator /> <ContextMenuRadioGroup value="pedro"> <ContextMenuRadioItem value="pedro"> Pedro Duarte </ContextMenuRadioItem> <ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem> </ContextMenuRadioGroup> </ContextMenuGroup> </ContextMenuContent> </ContextMenu> ); }; export default ContextMenuDemo;
npx shadcn-solid@latest add context-menu
npm install @kobalte/core
import { cn } from "@/libs/cn"; import type { ContextMenuCheckboxItemProps, ContextMenuContentProps, ContextMenuGroupLabelProps, ContextMenuItemLabelProps, ContextMenuItemProps, ContextMenuRadioItemProps, ContextMenuSeparatorProps, ContextMenuSubContentProps, ContextMenuSubTriggerProps, } from "@kobalte/core/context-menu"; import { ContextMenu as ContextMenuPrimitive } from "@kobalte/core/context-menu"; import type { PolymorphicProps } from "@kobalte/core/polymorphic"; import type { ComponentProps, ParentProps, ValidComponent, VoidProps, } from "solid-js"; import { splitProps } from "solid-js"; export const ContextMenu = ContextMenuPrimitive; export const ContextMenuTrigger = ContextMenuPrimitive.Trigger; export const ContextMenuGroup = ContextMenuPrimitive.Group; export const ContextMenuSub = ContextMenuPrimitive.Sub; export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup; type contextMenuSubTriggerProps<T extends ValidComponent = "div"> = ParentProps< ContextMenuSubTriggerProps<T> & { class?: string; inset?: boolean; } >; export const ContextMenuSubTrigger = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuSubTriggerProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuSubTriggerProps, [ "class", "children", "inset", ]); return ( <ContextMenuPrimitive.SubTrigger class={cn( "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[expanded]:bg-accent data-[expanded]:text-accent-foreground", local.inset && "pl-8", local.class, )} {...rest} > {local.children} <svg xmlns="http://www.w3.org/2000/svg" class="ml-auto h-4 w-4" viewBox="0 0 24 24" > <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m9 6l6 6l-6 6" /> <title>Arrow</title> </svg> </ContextMenuPrimitive.SubTrigger> ); }; type contextMenuSubContentProps<T extends ValidComponent = "div"> = ContextMenuSubContentProps<T> & { class?: string; }; export const ContextMenuSubContent = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuSubContentProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuSubContentProps, [ "class", ]); return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.SubContent class={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95", local.class, )} {...rest} /> </ContextMenuPrimitive.Portal> ); }; type contextMenuContentProps<T extends ValidComponent = "div"> = ContextMenuContentProps<T> & { class?: string; }; export const ContextMenuContent = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuContentProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuContentProps, ["class"]); return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content class={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md transition-shadow focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95", local.class, )} {...rest} /> </ContextMenuPrimitive.Portal> ); }; type contextMenuItemProps<T extends ValidComponent = "div"> = ContextMenuItemProps<T> & { class?: string; inset?: boolean; }; export const ContextMenuItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuItemProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuItemProps, [ "class", "inset", ]); return ( <ContextMenuPrimitive.Item class={cn( "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", local.inset && "pl-8", local.class, )} {...rest} /> ); }; type contextMenuCheckboxItemProps<T extends ValidComponent = "div"> = ParentProps< ContextMenuCheckboxItemProps<T> & { class?: string; } >; export const ContextMenuCheckboxItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuCheckboxItemProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuCheckboxItemProps, [ "class", "children", ]); return ( <ContextMenuPrimitive.CheckboxItem class={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", local.class, )} {...rest} > <ContextMenuPrimitive.ItemIndicator class="absolute left-2 inline-flex h-3.5 w-3.5 items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-4 w-4" > <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m5 12l5 5L20 7" /> <title>Checkbox</title> </svg> </ContextMenuPrimitive.ItemIndicator> {local.children} </ContextMenuPrimitive.CheckboxItem> ); }; type contextMenuRadioItemProps<T extends ValidComponent = "div"> = ParentProps< ContextMenuRadioItemProps<T> & { class?: string; } >; export const ContextMenuRadioItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuRadioItemProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuRadioItemProps, [ "class", "children", ]); return ( <ContextMenuPrimitive.RadioItem class={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", local.class, )} {...rest} > <ContextMenuPrimitive.ItemIndicator class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-2 w-2" > <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" > <path d="M0 0h24v24H0z" /> <path fill="currentColor" d="M7 3.34a10 10 0 1 1-4.995 8.984L2 12l.005-.324A10 10 0 0 1 7 3.34" /> </g> <title>Radio</title> </svg> </ContextMenuPrimitive.ItemIndicator> {local.children} </ContextMenuPrimitive.RadioItem> ); }; type contextMenuItemLabelProps<T extends ValidComponent = "div"> = ContextMenuItemLabelProps<T> & { class?: string; inset?: boolean; }; export const ContextMenuItemLabel = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuItemLabelProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuItemLabelProps, [ "class", "inset", ]); return ( <ContextMenuPrimitive.ItemLabel class={cn( "px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class, )} {...rest} /> ); }; type contextMenuGroupLabelProps<T extends ValidComponent = "span"> = ContextMenuGroupLabelProps<T> & { class?: string; inset?: boolean; }; export const ContextMenuGroupLabel = <T extends ValidComponent = "span">( props: PolymorphicProps<T, contextMenuGroupLabelProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuGroupLabelProps, [ "class", "inset", ]); return ( <ContextMenuPrimitive.GroupLabel as="div" class={cn( "px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class, )} {...rest} /> ); }; type contextMenuSeparatorProps<T extends ValidComponent = "hr"> = VoidProps< ContextMenuSeparatorProps<T> & { class?: string; } >; export const ContextMenuSeparator = <T extends ValidComponent = "hr">( props: PolymorphicProps<T, contextMenuSeparatorProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuSeparatorProps, [ "class", ]); return ( <ContextMenuPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-border", local.class)} {...rest} /> ); }; export const ContextMenuShortcut = (props: ComponentProps<"span">) => { const [local, rest] = splitProps(props, ["class"]); return ( <span class={cn( "ml-auto text-xs tracking-widest text-muted-foreground", local.class, )} {...rest} /> ); };
import { cn } from "@/libs/cn"; import type { ContextMenuCheckboxItemProps, ContextMenuContentProps, ContextMenuGroupLabelProps, ContextMenuItemLabelProps, ContextMenuItemProps, ContextMenuRadioItemProps, ContextMenuSeparatorProps, ContextMenuSubContentProps, ContextMenuSubTriggerProps, } from "@kobalte/core/context-menu"; import { ContextMenu as ContextMenuPrimitive } from "@kobalte/core/context-menu"; import type { PolymorphicProps } from "@kobalte/core/polymorphic"; import type { ComponentProps, ParentProps, ValidComponent, VoidProps, } from "solid-js"; import { splitProps } from "solid-js"; export const ContextMenu = ContextMenuPrimitive; export const ContextMenuTrigger = ContextMenuPrimitive.Trigger; export const ContextMenuGroup = ContextMenuPrimitive.Group; export const ContextMenuSub = ContextMenuPrimitive.Sub; export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup; type contextMenuSubTriggerProps<T extends ValidComponent = "div"> = ParentProps< ContextMenuSubTriggerProps<T> & { class?: string; inset?: boolean; } >; export const ContextMenuSubTrigger = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuSubTriggerProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuSubTriggerProps, [ "class", "children", "inset", ]); return ( <ContextMenuPrimitive.SubTrigger class={cn( "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:(bg-accent text-accent-foreground) data-[expanded]:(bg-accent text-accent-foreground)", local.inset && "pl-8", local.class, )} {...rest} > {local.children} <svg xmlns="http://www.w3.org/2000/svg" class="ml-auto h-4 w-4" viewBox="0 0 24 24" > <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m9 6l6 6l-6 6" /> <title>Arrow</title> </svg> </ContextMenuPrimitive.SubTrigger> ); }; type contextMenuSubContentProps<T extends ValidComponent = "div"> = ContextMenuSubContentProps<T> & { class?: string; }; export const ContextMenuSubContent = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuSubContentProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuSubContentProps, [ "class", ]); return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.SubContent class={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[expanded]:(animate-in fade-in-0 zoom-in-95) data-[closed]:(animate-out fade-out-0 zoom-out-95)", local.class, )} {...rest} /> </ContextMenuPrimitive.Portal> ); }; type contextMenuContentProps<T extends ValidComponent = "div"> = ContextMenuContentProps<T> & { class?: string; }; export const ContextMenuContent = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuContentProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuContentProps, ["class"]); return ( <ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Content class={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[expanded]:(animate-in fade-in-0 zoom-in-95) data-[closed]:(animate-out fade-out-0 zoom-out-95) focus-visible:(outline-none ring-1.5 ring-ring) transition-shadow", local.class, )} {...rest} /> </ContextMenuPrimitive.Portal> ); }; type contextMenuItemProps<T extends ValidComponent = "div"> = ContextMenuItemProps<T> & { class?: string; inset?: boolean; }; export const ContextMenuItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuItemProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuItemProps, [ "class", "inset", ]); return ( <ContextMenuPrimitive.Item class={cn( "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:(bg-accent text-accent-foreground) data-[disabled]:(pointer-events-none opacity-50)", local.inset && "pl-8", local.class, )} {...rest} /> ); }; type contextMenuCheckboxItemProps<T extends ValidComponent = "div"> = ParentProps< ContextMenuCheckboxItemProps<T> & { class?: string; } >; export const ContextMenuCheckboxItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuCheckboxItemProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuCheckboxItemProps, [ "class", "children", ]); return ( <ContextMenuPrimitive.CheckboxItem class={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:(bg-accent text-accent-foreground) data-[disabled]:(pointer-events-none opacity-50)", local.class, )} {...rest} > <ContextMenuPrimitive.ItemIndicator class="absolute left-2 inline-flex h-3.5 w-3.5 items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-4 w-4" > <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m5 12l5 5L20 7" /> <title>Checkbox</title> </svg> </ContextMenuPrimitive.ItemIndicator> {local.children} </ContextMenuPrimitive.CheckboxItem> ); }; type contextMenuRadioItemProps<T extends ValidComponent = "div"> = ParentProps< ContextMenuRadioItemProps<T> & { class?: string; } >; export const ContextMenuRadioItem = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuRadioItemProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuRadioItemProps, [ "class", "children", ]); return ( <ContextMenuPrimitive.RadioItem class={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:(bg-accent text-accent-foreground) data-[disabled]:(pointer-events-none opacity-50)", local.class, )} {...rest} > <ContextMenuPrimitive.ItemIndicator class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-2 w-2" > <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" > <path d="M0 0h24v24H0z" /> <path fill="currentColor" d="M7 3.34a10 10 0 1 1-4.995 8.984L2 12l.005-.324A10 10 0 0 1 7 3.34" /> </g> <title>Radio</title> </svg> </ContextMenuPrimitive.ItemIndicator> {local.children} </ContextMenuPrimitive.RadioItem> ); }; type contextMenuItemLabelProps<T extends ValidComponent = "div"> = ContextMenuItemLabelProps<T> & { class?: string; inset?: boolean; }; export const ContextMenuItemLabel = <T extends ValidComponent = "div">( props: PolymorphicProps<T, contextMenuItemLabelProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuItemLabelProps, [ "class", "inset", ]); return ( <ContextMenuPrimitive.ItemLabel class={cn( "px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class, )} {...rest} /> ); }; type contextMenuGroupLabelProps<T extends ValidComponent = "span"> = ContextMenuGroupLabelProps<T> & { class?: string; inset?: boolean; }; export const ContextMenuGroupLabel = <T extends ValidComponent = "span">( props: PolymorphicProps<T, contextMenuGroupLabelProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuGroupLabelProps, [ "class", "inset", ]); return ( <ContextMenuPrimitive.GroupLabel as="div" class={cn( "px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class, )} {...rest} /> ); }; type contextMenuSeparatorProps<T extends ValidComponent = "hr"> = VoidProps< ContextMenuSeparatorProps<T> & { class?: string; } >; export const ContextMenuSeparator = <T extends ValidComponent = "hr">( props: PolymorphicProps<T, contextMenuSeparatorProps<T>>, ) => { const [local, rest] = splitProps(props as contextMenuSeparatorProps, [ "class", ]); return ( <ContextMenuPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-border", local.class)} {...rest} /> ); }; export const ContextMenuShortcut = (props: ComponentProps<"span">) => { const [local, rest] = splitProps(props, ["class"]); return ( <span class={cn( "ml-auto text-xs tracking-widest text-muted-foreground", local.class, )} {...rest} /> ); };
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from "@/components/ui/context-menu";
<ContextMenu> <ContextMenuTrigger>Right click</ContextMenuTrigger> <ContextMenuContent> <ContextMenuItem>Profile</ContextMenuItem> <ContextMenuItem>Billing</ContextMenuItem> <ContextMenuItem>Team</ContextMenuItem> <ContextMenuItem>Subscription</ContextMenuItem> </ContextMenuContent> </ContextMenu>