fetaUI
GitHub

Dropdown Menu

A list of actions behind a button, with shortcuts, toggles, submenus and a red destructive action at the end.

FreeWeb and MobileMotion

Preview

npx shadcn add @fetaui/dropdown-menu
Playground
Open
Shortcuts
Submenu
Toggles
Account
Menu
Align
Icons animation
Motion

Installation

Terminal· bash
 npx shadcn add @fetaui/dropdown-menu

Accessibility

  • The trigger has aria-haspopup and aria-expanded.
  • Rows are menuitem, menuitemcheckbox or menuitemradio with aria-checked.
  • Shortcuts are exposed with aria-keyshortcuts.
  • Disabled rows stay focusable with aria-disabled, so they can still be read.

Motion

  • Opens in 160 ms from the trigger, scaling from 0.96 and dropping 4 px. Closes with a 100 ms fade.
  • The highlight glides between rows in 100 ms, by pointer or keys.
  • A chosen action flashes its wash, then the menu closes.
  • Submenus slide in 8 px over 180 ms. Checks draw and radio dots pop, and the menu stays open.
  • With reduced motion, the menu and submenus appear in place and toggles swap.

Responsive behavior

  • 240 wide on Web. Full width up to 358 on Mobile, with 46 px rows and no keycaps.
  • Submenus open beside the row on Web, flipping at the screen edge, and drill in with a back row on Mobile.

Performance

  • The panel mounts only while open. Check and radio values sit in a store above it, so they survive closing.
  • One passive pointermove listener serves every menu, so a layout shift under a resting pointer never moves the highlight.
  • The highlight is one shared layout element that glides between rows.

Note for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Import DropdownMenu from @/components/fetaui/dropdown-menu after installing it. Menu and useMenuStore give the bare panel.
  • entries mixes actions, checkbox, radio, label, separator and custom rows. An action with items opens a submenu to the side on Web and in place on Mobile.
  • trigger is a render prop: spread its props on any button. avatar turns the default trigger into the Account pill.
  • The rows in the demo have no handlers, so pass onSelect. Composes Button, Kbd and the shared label motion.

Related