A composable context menu opened by right-clicking on desktop or long-pressing on mobile, with nested submenus, selectable items, shortcuts, and icon-friendly actions.
Preview
Right-click on desktop or long-press on mobile
context-menu.demo.svelte
<scriptlang="ts">import*as ContextMenu from'$lib/components/ui/context-menu';</script><ContextMenu.Root> <ContextMenu.Triggerclass="flex h-40 w-full max-w-sm items-center justify-center rounded-lg border border-dashed border-border bg-muted/30 text-sm text-muted-foreground select-none" > Right-click on desktop or long-press on mobile </ContextMenu.Trigger> <ContextMenu.Contentclass="w-52"> <ContextMenu.Item>Open</ContextMenu.Item> <ContextMenu.Item>Rename</ContextMenu.Item> <ContextMenu.Item> Duplicate <ContextMenu.Shortcut>⌘D</ContextMenu.Shortcut> </ContextMenu.Item> <ContextMenu.Separator /> <ContextMenu.Itemvariant="destructive">Delete</ContextMenu.Item> </ContextMenu.Content></ContextMenu.Root>
Compose Root, Trigger, Content, and Item using the ContextMenu namespace. Tell users to right-click the trigger on desktop or long-press it on mobile. Add Sub, SubTrigger, and SubContent for any depth of nested menu. CheckboxItem and RadioGroup provide persistent selection state through Svelte bindings. For action icons, import components from the icon library configured by shadcn and render them directly inside an Item or SubTrigger; the menu automatically sizes and aligns SVG icons.
API reference
Prop
Type
Default
Description
Root.open
boolean (bindable)
false
Controls whether the root menu is open.
Trigger
Bits UI trigger props
—
Element that opens the component through its supported interaction.
Content
Bits UI content props
—
Floating surface that contains the component content.
Item.variant
default | destructive
default
Selects the visual treatment for an individual item.
Item.inset
boolean
false
Adds leading inset spacing to align an item with icon-bearing actions.
CheckboxItem.checked
boolean (bindable)
false
Controls whether the checkbox item is selected.
RadioGroup.value
string (bindable)
''
Value of the currently selected radio item.
Sub.open
boolean (bindable)
false
Controls whether the nested submenu is open.
class
string
—
Tailwind CSS classes applied to the component root.
Source
These files are included in the registry item and copied into the consuming project.
context-menu.svelte
<scriptlang="ts">import { ContextMenu as ContextMenuPrimitive } from'bits-ui';let { open = $bindable(false), ...restProps }:ContextMenuPrimitive.RootProps= $props();</script><ContextMenuPrimitive.Rootbind:open{...restProps} />
import CheckboxItem from'./context-menu-checkbox-item.svelte';import Content from'./context-menu-content.svelte';import GroupHeading from'./context-menu-group-heading.svelte';import Group from'./context-menu-group.svelte';import Item from'./context-menu-item.svelte';import Label from'./context-menu-label.svelte';import Portal from'./context-menu-portal.svelte';import RadioGroup from'./context-menu-radio-group.svelte';import RadioItem from'./context-menu-radio-item.svelte';import Separator from'./context-menu-separator.svelte';import Shortcut from'./context-menu-shortcut.svelte';import SubContent from'./context-menu-sub-content.svelte';import SubTrigger from'./context-menu-sub-trigger.svelte';import Sub from'./context-menu-sub.svelte';import Trigger from'./context-menu-trigger.svelte';import Root from'./context-menu.svelte';export { Root, Sub, Portal, Item, GroupHeading, Label, Group, Trigger, Content, Shortcut, Separator, RadioItem, SubContent, SubTrigger, RadioGroup, CheckboxItem,// Root as ContextMenu, Sub as ContextMenuSub, Portal as ContextMenuPortal, Item as ContextMenuItem, GroupHeading as ContextMenuGroupHeading, Group as ContextMenuGroup, Content as ContextMenuContent, Trigger as ContextMenuTrigger, Shortcut as ContextMenuShortcut, RadioItem as ContextMenuRadioItem, Separator as ContextMenuSeparator, RadioGroup as ContextMenuRadioGroup, SubContent as ContextMenuSubContent, SubTrigger as ContextMenuSubTrigger, CheckboxItem as ContextMenuCheckboxItem, Label as ContextMenuLabel};