motif svelteArc

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.

Live specimen

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.

Installation

pnpm dlx shadcn-svelte@latest add @arcui/dropdown-menu

Usage

<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

PropTypeDefaultDescription
labelstring—Trigger text. In Phase 2, a new label rises in while the trigger width springs.
itemsDropdownItem[]—Items array: { label, onSelect?, disabled?, icon?: Snippet, destructive?, separatorBefore? }.
iconSnippet—Optional leading icon snippet rendered in the trigger button.

Keyboard Interactions

KeyAction
Enter / Space / ArrowDownOpens the menu from the trigger.
ArrowDown / ArrowUpMoves focus between menu items, looping at the ends.
Home / EndJumps directly to the first or last menu item.
EscapeCloses 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