Dropdown Menu
A popover menu of actions revealed from a trigger. Reach for it to group secondary actions (edit, share, delete) behind a single affordance such as a kebab or avatar button. Pass a declarative items array for the common case, or children for fully custom content. Anatomy: a trigger snippet opens a floating Content panel that renders Item rows and optional Separator dividers.
Declarative items
items is the usual way in: a row per entry, separator: true for a divider, destructive: true for the one that cannot be taken back. A menu built from data can't drift row to row the way hand-written markup does.
<DropdownMenu
items={[
{ label: 'View order', onSelect: () => {} },
{ label: 'Print invoice', onSelect: () => {} },
{ separator: true },
{ label: 'Resend receipt', disabled: true },
{ label: 'Refund', destructive: true, onSelect: () => {} }
]}
>
{#snippet trigger()}
<span
class="text-muted hover:bg-hover hover:text-foreground inline-flex size-9 items-center justify-center rounded-md"
>
<MoreHorizontal class="size-4" />
</span>
{/snippet}
</DropdownMenu>Alignment and width
align sets which edge of the trigger the panel lines up with, and sideOffset={0} seats it flush against the trigger, select-style. contentClass is where a fixed width goes when the rows would otherwise be as narrow as their longest label.
<DropdownMenu
align="end"
sideOffset={0}
contentClass="w-56"
items={[
{ label: 'Export as CSV', onSelect: () => {} },
{ label: 'Export as Excel', onSelect: () => {} }
]}
>
{#snippet trigger()}
<Button variant="secondary">Export</Button>
{/snippet}
</DropdownMenu>Custom content
Passing children replaces the row list entirely, for a panel that isn't a list of actions. Anything interactive in there still needs a highlight of its own — menu rows use data-highlighted:bg-hover, since a keyboard user has nothing else to go on.
<DropdownMenu align="start" contentClass="w-64">
{#snippet trigger()}
<Button variant="secondary">Account</Button>
{/snippet}
<div class="px-3 py-2">
<p class="text-sm font-medium">Ada Lovelace</p>
<p class="text-muted truncate text-xs">ada@example.com</p>
</div>
<div class="p-1">
<button class="hover:bg-hover w-full rounded-md px-2 py-1.5 text-left text-sm">
Sign out
</button>
</div>
</DropdownMenu>API
| Prop | Type | Default | Description |
|---|---|---|---|
children | import('svelte').Snippet | Optional snippet for fully custom Content. When provided it replaces the | |
open bindable | boolean | false | Bindable open state of the menu popover. |
trigger required | import('svelte').Snippet | Required snippet rendered as the clickable element that opens the menu (e.g. a kebab button or avatar). | |
items | Array<{label: string, onSelect?: Function, disabled?: boolean, separator?: boolean, destructive?: boolean}> | [] | Declarative menu rows. Set |
align | 'start'|'center'|'end' | 'end' | Alignment of the Content panel relative to the trigger. |
sideOffset | number | 4 | Gap in pixels between the trigger and the Content panel. |
triggerClass | string | Additional Tailwind classes merged onto the trigger, e.g. | |
triggerProps | Record<string, unknown> | Extra attributes spread onto the trigger button, e.g. | |
contentClass | string | Additional Tailwind classes merged onto the Content panel, e.g. a fixed width. |