ContextMenu
Display a floating menu of actions triggered by right-click. Supports action items, checkbox items, radio groups, submenus, keyboard shortcuts, separators, and labels.
Basic Usage
Wrap any element with ContextMenu and pass an array of items. The menu appears on right-click.
vacation-photo.jpg
2.4 MB · Modified 2 days ago
<script lang="ts">
import { ContextMenu, Modal, Button } from 'sv5ui';
let showDeleteModal = $state(false);
const items = [
{ label: 'Edit', icon: 'lucide:pencil' },
{ label: 'Duplicate', icon: 'lucide:copy' },
{ label: 'Archive', icon: 'lucide:archive' },
{ label: 'Delete', icon: 'lucide:trash-2', color: 'error', onSelect: () => showDeleteModal = true }
];
</script>
<ContextMenu {items}>
<div class="rounded-xl border p-5">
<p class="font-medium">vacation-photo.jpg</p>
<p class="text-sm opacity-50">2.4 MB · Modified 2 days ago</p>
</div>
</ContextMenu>
<Modal
bind:open={showDeleteModal}
title="Delete file?"
description="This will permanently delete vacation-photo.jpg."
>
{#snippet footer()}
<div class="flex justify-end gap-2">
<Button variant="outline" color="surface" label="Cancel" onclick={() => showDeleteModal = false} />
<Button variant="solid" color="error" label="Delete" onclick={() => showDeleteModal = false} />
</div>
{/snippet}
</Modal>With Keyboard Shortcuts
Use the kbds prop on items to display keyboard shortcut hints.
Right-click to see editing shortcuts
<script lang="ts">
import { ContextMenu } from 'sv5ui';
const items = [
{ label: 'Undo', icon: 'lucide:undo-2', kbds: ['meta', 'z'] },
{ label: 'Redo', icon: 'lucide:redo-2', kbds: ['meta', 'shift', 'z'] },
{ type: 'separator' },
{ label: 'Cut', icon: 'lucide:scissors', kbds: ['meta', 'x'] },
{ label: 'Copy', icon: 'lucide:copy', kbds: ['meta', 'c'] },
{ label: 'Paste', icon: 'lucide:clipboard', kbds: ['meta', 'v'] }
];
</script>
<ContextMenu {items}>
<div class="rounded-xl border p-5">
<!-- Text editor area -->
Right-click to see editing shortcuts
</div>
</ContextMenu>Checkbox Items
Set type: 'checkbox' on items to render toggleable checkboxes. Use closeOnSelect: false to keep the menu open while toggling.
Canvas element
<script lang="ts">
import { ContextMenu } from 'sv5ui';
let showGrid = $state(true);
let showRulers = $state(false);
let snapToGrid = $state(true);
let items = $derived([
{ type: 'label', label: 'View Options' },
{ type: 'separator' },
{ type: 'checkbox', label: 'Show Grid', checked: showGrid, closeOnSelect: false, onCheckedChange: (v) => showGrid = v },
{ type: 'checkbox', label: 'Show Rulers', checked: showRulers, closeOnSelect: false, onCheckedChange: (v) => showRulers = v },
{ type: 'checkbox', label: 'Snap to Grid', checked: snapToGrid, closeOnSelect: false, onCheckedChange: (v) => snapToGrid = v }
]);
</script>
<ContextMenu {items}>
<div class="rounded-xl border p-5">
<!-- Canvas workspace -->
</div>
</ContextMenu>Radio Items
Use type: 'radio' items with the radioGroups prop for single-select groups.
<script lang="ts">
import { ContextMenu } from 'sv5ui';
let sortBy = $state('name');
const items = [
{ type: 'label', label: 'Sort By' },
{ type: 'separator' },
{ type: 'radio', label: 'Name', value: 'name' },
{ type: 'radio', label: 'Date Modified', value: 'date' },
{ type: 'radio', label: 'Size', value: 'size' }
];
const radioGroups = [
{
name: 'sortBy',
value: sortBy,
onValueChange: (v) => sortBy = v
}
];
</script>
<ContextMenu {items} {radioGroups}>
<div class="rounded-xl border">
<!-- File list -->
</div>
</ContextMenu>Submenu
Use type: 'sub' with a nested items array to create flyout submenus.
Project Files
3 items · 4.8 MB
<script lang="ts">
import { ContextMenu } from 'sv5ui';
const items = [
{ label: 'New File', icon: 'lucide:file-plus' },
{ label: 'New Folder', icon: 'lucide:folder-plus' },
{ type: 'separator' },
{
type: 'sub',
label: 'Share',
icon: 'lucide:share-2',
items: [
{ label: 'Email', icon: 'lucide:mail' },
{ label: 'Message', icon: 'lucide:message-square' },
{ type: 'separator' },
{ label: 'More...', icon: 'lucide:more-horizontal' }
]
},
{ type: 'separator' },
{ label: 'Delete', icon: 'lucide:trash-2', color: 'error' }
];
</script>
<ContextMenu {items}>
<div class="rounded-xl border p-5">
<!-- Folder card -->
</div>
</ContextMenu>Sizes
5 sizes from extra small to extra large.
<ContextMenu {items} size="xs">...</ContextMenu>
<ContextMenu {items} size="sm">...</ContextMenu>
<ContextMenu {items} size="md">...</ContextMenu>
<ContextMenu {items} size="lg">...</ContextMenu>
<ContextMenu {items} size="xl">...</ContextMenu>Disabled Items
Set disabled: true on individual items to prevent interaction.
Read-only Document
Some actions are unavailable for this item.
<script lang="ts">
import { ContextMenu } from 'sv5ui';
const items = [
{ label: 'Edit', icon: 'lucide:pencil' },
{ label: 'Duplicate', icon: 'lucide:copy', disabled: true },
{ label: 'Archive', icon: 'lucide:archive', disabled: true },
{ label: 'Delete', icon: 'lucide:trash-2', color: 'error' }
];
</script>
<ContextMenu {items}>
<div class="rounded-xl border p-5">
<!-- Read-only document -->
</div>
</ContextMenu>UI Slots
Use the ui prop to override classes on specific internal elements of the ContextMenu.
| Slot | Description |
|---|---|
content | The floating menu panel — controls background, border, shadow, and rounding |
group | Groups related menu items together — controls spacing between groups |
separator | Visual divider between groups — controls color and spacing |
label | Non-interactive heading within a group — controls font size, weight, color |
item | Individual menu item row — controls padding, hover state, cursor |
itemIcon | Icon element within a menu item — controls icon size and color |
itemLabel | Text label within a menu item — controls font size and color |
itemKbd | Keyboard shortcut badge — controls font, background, rounding |
itemIndicator | Indicator for checkbox/radio items — controls size and color |
subTrigger | Submenu trigger item — controls hover state and arrow indicator |
subTriggerIcon | Arrow icon on the submenu trigger — controls icon size and color |
subContent | Submenu floating panel — controls background, border, shadow |
checkboxIndicator | Checkbox check mark indicator — controls icon and color |
radioIndicator | Radio dot indicator — controls icon and color |
Snippets
Use Svelte 5 snippets to customize specific parts of the ContextMenu.
| Snippet | Type | Description |
|---|---|---|
children | Snippet<[{ open: boolean }]> | Right-click target area |
content | Snippet<[{ open: boolean, close: () => void }]> | Custom content replacing default item rendering |
header | Snippet<[{ close: () => void }]> | Custom header above the menu items |
footer | Snippet<[{ close: () => void }]> | Custom footer below the menu items |
item | Snippet<[ContextMenuItemSlotProps]> | Custom renderer for individual menu items |
itemLeading | Snippet<[ContextMenuItemSlotProps]> | Custom leading section of menu items |
itemLabel | Snippet<[ContextMenuItemSlotProps]> | Custom label section of menu items |
itemTrailing | Snippet<[ContextMenuItemSlotProps]> | Custom trailing section of menu items |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | |
items | ContextMenuItem[] | [] | |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | |
loop | boolean | true | |
radioGroups | RadioGroup[] | [] | |
transition | boolean | true | |
portal | boolean | true | |
checkedIcon | string | 'lucide:check' | |
submenuIcon | string | 'lucide:chevron-right' | |
class | string | - | |
ui | Record<Slot, Class> | - |