Dropdown Menu

A menu of actions displayed to the user when a trigger is activated. Supports items, checkboxes, radio groups, sub-menus, and keyboard shortcuts.

Installation

pnpm
pnpm dlx @hex-core/cli add dropdown-menu

Grouped row actions with a destructive item

Use DropdownMenuGroup + DropdownMenuLabel to cluster related actions, separators between clusters, and a danger-styled final item

tsx
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="ghost" size="icon" aria-label="Row actions">…</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent align="end" className="w-56">
    <DropdownMenuLabel>Quick actions</DropdownMenuLabel>
    <DropdownMenuGroup>
      <DropdownMenuItem onSelect={() => onOpen(row.id)}>
        Open
        <DropdownMenuShortcut>↵</DropdownMenuShortcut>
      </DropdownMenuItem>
      <DropdownMenuItem onSelect={() => onDuplicate(row.id)}>
        Duplicate
        <DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
      </DropdownMenuItem>
    </DropdownMenuGroup>
    <DropdownMenuSeparator />
    <DropdownMenuLabel>Sharing</DropdownMenuLabel>
    <DropdownMenuGroup>
      <DropdownMenuItem onSelect={() => onCopyLink(row.id)}>Copy link</DropdownMenuItem>
      <DropdownMenuItem onSelect={() => onShare(row.id)}>Invite people…</DropdownMenuItem>
    </DropdownMenuGroup>
    <DropdownMenuSeparator />
    <DropdownMenuItem className="text-destructive" onSelect={() => onDelete(row.id)}>
      Delete
      <DropdownMenuShortcut>⌫</DropdownMenuShortcut>
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

With checkbox + radio items

Toggle a side panel with DropdownMenuCheckboxItem and pick a single position with a DropdownMenuRadioGroup

tsx
const [showPanel, setShowPanel] = useState(true);
const [position, setPosition] = useState("right");

return (
  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <Button variant="outline">View</Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent className="w-56">
      <DropdownMenuLabel>Layout</DropdownMenuLabel>
      <DropdownMenuCheckboxItem checked={showPanel} onCheckedChange={setShowPanel}>
        Show panel
      </DropdownMenuCheckboxItem>
      <DropdownMenuSeparator />
      <DropdownMenuLabel>Panel position</DropdownMenuLabel>
      <DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
        <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
        <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
        <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
        <DropdownMenuRadioItem value="left">Left</DropdownMenuRadioItem>
      </DropdownMenuRadioGroup>
    </DropdownMenuContent>
  </DropdownMenu>
);

API Reference

PropTypeDefaultDescription
open
booleanControlled open state
defaultOpen
booleanfalseDefault open state
onOpenChange
functionCallback on open state change: (open: boolean) => void
modal
booleantrueWhen true, interaction outside the menu is blocked

AI Guidance

When to use

Use for action menus triggered by a button: user menus, row-action menus, toolbar overflow. Include DropdownMenuLabel for context, DropdownMenuSeparator for grouping.

When not to use

Don't use for navigation between pages (use NavigationMenu or links). Don't use for selection inputs (use Select or Combobox). Don't use for right-click menus (use ContextMenu).

Common mistakes

  • Using DropdownMenu as a form Select (use Select instead)
  • Putting interactive elements directly in the trigger without asChild
  • Too many items without grouping (use DropdownMenuLabel + DropdownMenuSeparator)
  • Forgetting DropdownMenuShortcut for keyboard-accessible actions

Accessibility

Full keyboard navigation: arrow keys, Home, End, typeahead, Escape. Radix handles role='menu', role='menuitem', and aria-labelledby.

Related components

Token budget: 700