Context Menu

Opens at the pointer on right-click or a tap on touch screens. From the keyboard, focus the trigger and press the Menu key or Shift+F10.

<script lang="ts">
    import * as ContextMenu from '@sivir-ui/svelte/components/context-menu';
    import Shortcut from '@sivir-ui/svelte/components/shortcut';
</script>

<div class="flex items-center justify-center">
    <ContextMenu.Root>
        <ContextMenu.Trigger>
            <div
                class="flex h-28 w-64 items-center justify-center rounded-[var(--radius-lg)] border border-dashed border-border bg-secondary/30 text-sm text-foreground-muted"
            >
                Right-click or tap here
            </div>
        </ContextMenu.Trigger>

        <ContextMenu.Content class="min-w-[13rem]">
            <ContextMenu.Item callback={() => {}}>
                Copy
                <Shortcut shortcut="cmd+C" />
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                Paste
                <Shortcut shortcut="cmd+V" />
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                Duplicate
                <Shortcut shortcut="cmd+D" />
            </ContextMenu.Item>

            <ContextMenu.Sub>
                <ContextMenu.SubTrigger>Arrange</ContextMenu.SubTrigger>
                <ContextMenu.SubContent class="min-w-[12rem]">
                    <ContextMenu.Item callback={() => {}}>
                        Bring to front
                        <Shortcut shortcut="cmd+]" />
                    </ContextMenu.Item>
                    <ContextMenu.Item callback={() => {}}>
                        Send to back
                        <Shortcut shortcut="cmd+[" />
                    </ContextMenu.Item>
                </ContextMenu.SubContent>
            </ContextMenu.Sub>

            <ContextMenu.Item callback={() => {}}>
                <span class="text-[var(--color-error)]">Delete</span>
                <Shortcut shortcut="backspace" />
            </ContextMenu.Item>
        </ContextMenu.Content>
    </ContextMenu.Root>
</div>

Installation

Copy the command below and run it in your terminal.

bunx @sivir-ui/svelte add context-menu

Usage

Pass the action as callback; ContextMenu.Item ignores onclick. CheckboxItem requires a unique value.

import * as ContextMenu from '@sivir-ui/svelte/components/context-menu';

let zoom = $state(1);
let showGrid = $state(true);

<ContextMenu.Root>
  <ContextMenu.Trigger>
    <div>Canvas</div>
  </ContextMenu.Trigger>
  <ContextMenu.Content>
    <ContextMenu.Item callback={() => (zoom = 1)}>Reset zoom</ContextMenu.Item>
    <ContextMenu.CheckboxItem value="grid" bind:checked={showGrid}>Show grid</ContextMenu.CheckboxItem>
  </ContextMenu.Content>
</ContextMenu.Root>

Examples

File actions

<script lang="ts">
    import FolderOpen from '@lucide/svelte/icons/folder-open';
    import Pencil from '@lucide/svelte/icons/pencil';
    import Trash from '@lucide/svelte/icons/trash-2';
    import * as ContextMenu from '@sivir-ui/svelte/components/context-menu';
</script>

<div class="flex items-center justify-center">
    <ContextMenu.Root>
        <ContextMenu.Trigger>
            <div
                class="flex h-20 w-56 items-center justify-center rounded-[var(--radius-lg)] border border-dashed border-border bg-secondary/30 text-sm text-foreground-muted"
            >
                Right-click a file
            </div>
        </ContextMenu.Trigger>
        <ContextMenu.Content>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><FolderOpen size={14} /> Open</span>
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><Pencil size={14} /> Rename</span>
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2 text-[var(--color-error)]">
                    <Trash size={14} />
                    Delete
                </span>
            </ContextMenu.Item>
        </ContextMenu.Content>
    </ContextMenu.Root>
</div>

Image actions

<script lang="ts">
    import Copy from '@lucide/svelte/icons/copy';
    import Download from '@lucide/svelte/icons/download';
    import * as ContextMenu from '@sivir-ui/svelte/components/context-menu';
</script>

<div class="flex items-center justify-center">
    <ContextMenu.Root>
        <ContextMenu.Trigger>
            <div
                class="flex h-20 w-56 items-center justify-center rounded-[var(--radius-lg)] border border-dashed border-border bg-secondary/30 text-sm text-foreground-muted"
            >
                Right-click an image
            </div>
        </ContextMenu.Trigger>
        <ContextMenu.Content>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><Download size={14} /> Save image</span>
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><Copy size={14} /> Copy image</span>
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>Open in new tab</ContextMenu.Item>
        </ContextMenu.Content>
    </ContextMenu.Root>
</div>

Task actions

<script lang="ts">
    import Check from '@lucide/svelte/icons/check';
    import Pencil from '@lucide/svelte/icons/pencil';
    import Share from '@lucide/svelte/icons/share-2';
    import * as ContextMenu from '@sivir-ui/svelte/components/context-menu';
</script>

<div class="flex items-center justify-center">
    <ContextMenu.Root>
        <ContextMenu.Trigger>
            <div
                class="flex h-20 w-56 items-center justify-center rounded-[var(--radius-lg)] border border-dashed border-border bg-secondary/30 text-sm text-foreground-muted"
            >
                Right-click a task
            </div>
        </ContextMenu.Trigger>
        <ContextMenu.Content>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><Pencil size={14} /> Edit</span>
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><Share size={14} /> Share</span>
            </ContextMenu.Item>
            <ContextMenu.Item callback={() => {}}>
                <span class="flex items-center gap-2"><Check size={14} /> Mark complete</span>
            </ContextMenu.Item>
        </ContextMenu.Content>
    </ContextMenu.Root>
</div>

Anatomy

ContextMenu.Root - Provides context menu state.

ContextMenu.Trigger - Wraps the area that opens the menu.

ContextMenu.Content - Renders the menu surface.

ContextMenu.Item - Renders a menu action.

ContextMenu.CheckboxItem - Renders a checkable menu item.

ContextMenu.Separator - Separates menu items.

ContextMenu.Sub - Provides submenu state.

ContextMenu.SubTrigger - Opens a submenu.

ContextMenu.SubContent - Renders a submenu surface.