Dropdown Menu ported
A focused list of actions anchored to a trigger.
A labelled trigger that opens a floating menu with keyboard navigation, single-highlight gliding, and destructive action styling.
Loading demo…
Guidance
When to use
- A list of commands behind a labelled button, such as Rename, Duplicate, and Delete.
- Row or card actions where a visible trigger is clearer than right-click.
- Short grouped menus with separators and one destructive item at the end.
When not to use
- Use Split Button when one command is the default.
- Use Context Menu for right-click actions on content.
- Use User Menu for the account menu, and Command Palette for searching many commands.
pnpm dlx shadcn-svelte@latest add @arcui/dropdown-menuUsage
<script lang="ts">
import DropdownMenu from '$lib/components/dropdown-menu/dropdown-menu.svelte';
import Pencil from '@lucide/svelte/icons/pencil';
import Copy from '@lucide/svelte/icons/copy';
import Trash2 from '@lucide/svelte/icons/trash-2';
function rename() { console.log('Rename clicked'); }
function duplicate() { console.log('Duplicate clicked'); }
function remove() { console.log('Delete clicked'); }
</script>
{#snippet renameIcon()}<Pencil size={15} />{/snippet}
{#snippet duplicateIcon()}<Copy size={15} />{/snippet}
{#snippet deleteIcon()}<Trash2 size={15} />{/snippet}
<DropdownMenu
label="Actions"
items={[
{ label: 'Rename', icon: renameIcon, onSelect: rename },
{ label: 'Duplicate', icon: duplicateIcon, onSelect: duplicate },
{ label: 'Delete', icon: deleteIcon, onSelect: remove, destructive: true, separatorBefore: true }
]}
/>Variants & Examples
Default Project Actions
Standard trigger button with text label and items array.
<DropdownMenu label="Actions" items={actionItems} />With Trigger Icon
Leading icon rendered in the trigger alongside the label.
{#snippet icon()}<MoreHorizontal size={15} />{/snippet}
<DropdownMenu label="Options" {icon} items={actionItems} />Separators and Destructive Items
Groups related commands with visual dividers and danger styling.
<DropdownMenu
label="File"
items={[
{ label: 'Download', onSelect: download },
{ label: 'Move to Trash', destructive: true, separatorBefore: true, onSelect: remove }
]}
/>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Trigger text. In Phase 2, a new label rises in while the trigger width springs. |
items | DropdownItem[] | — | Items array: { label, onSelect?, disabled?, icon?: Snippet, destructive?, separatorBefore? }. |
icon | Snippet | — | Optional leading icon snippet rendered in the trigger button. |
Keyboard Interactions
| Key | Action |
|---|---|
Enter / Space / ArrowDown | Opens the menu from the trigger. |
ArrowDown / ArrowUp | Moves focus between menu items, looping at the ends. |
Home / End | Jumps directly to the first or last menu item. |
Escape | Closes the menu and returns focus cleanly to the trigger. |
Accessibility
- Built on Bits UI DropdownMenu (WAI-ARIA Menu button pattern): the trigger receives aria-haspopup="menu" and aria-expanded; items receive role="menuitem" and typeahead.
- Icons and gliding highlight elements are aria-hidden; destructive items are marked by color tone and should clearly state what they destroy.
- Disabled items are skipped during keyboard navigation.
Motion
Phase 1 still-state: trigger label renders measured width statically and the highlight renders at the active menuitem. Phase 2 springs the gliding highlight between items via motionTokens.spring.snappy for pointer hover, and springs trigger width on label changes with motionTokens.spring.morph. Reduced motion disables highlight gliding and scale transitions.
Notes for AI
- Default choice for a list of commands behind a button. Use Split Button when one command is the default.
- Items are passed as data objects, not children, so build the array from your application actions.
- Group items with separatorBefore; place destructive items at the end.
Source of truth: registry/components/dropdown-menu/dropdown-menu.tsx