Dropdown Menu
A menu of actions and toggles launched from a button.
@garn/ui/dropdown-menuOn this page
Default
Account menu with a label, items, shortcuts, and a separator.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add dropdown-menu- Registry
dropdown-menu- Deps
@radix-ui/react-dropdown-menulucide-react- Registry deps
utils
Import
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent } from "@garn/ui/dropdown-menu";Anatomy
The parts this component is built from — compose them to assemble it.
DropdownMenuRequiredRoot — owns the open state. No DOM of its own.
DropdownMenuTriggerRequired[data-slot="dropdown-menu-trigger"]The menu button (asChild to wrap your own button); exposes aria-haspopup / aria-expanded.
DropdownMenuContentRequired[data-slot="dropdown-menu-content"]The portaled, scrollable menu surface.
DropdownMenuItem[data-slot="dropdown-menu-item"]An action item; inset aligns it with checkbox/radio items. Selecting it closes the menu.
DropdownMenuCheckboxItem[data-slot="dropdown-menu-checkbox-item"]A toggleable item (controlled via checked + onCheckedChange).
DropdownMenuRadioGroupGroups radio items; owns the selected value.
DropdownMenuRadioItem[data-slot="dropdown-menu-radio-item"]A single-select item inside a RadioGroup.
DropdownMenuLabel[data-slot="dropdown-menu-label"]A non-interactive section heading; inset for alignment.
DropdownMenuSeparator[data-slot="dropdown-menu-separator"]Divider between groups.
DropdownMenuShortcut[data-slot="dropdown-menu-shortcut"]Right-aligned keyboard-shortcut hint (display only — doesn't bind the key).
DropdownMenuGroupSemantic grouping of related items.
DropdownMenuSubA submenu container (Sub / SubTrigger / SubContent).
DropdownMenuSubTrigger[data-slot="dropdown-menu-sub-trigger"]Opens a submenu; inset for alignment.
DropdownMenuSubContent[data-slot="dropdown-menu-sub-content"]The nested submenu surface.
Props
The component's public props and their types.
No component-specific props — this is a thin wrapper over its native element.
Styling
Target these data-slots and states, and remap these tokens, to restyle without forking the component.
[data-slot="dropdown-menu-content"]States
| Selector | State | Description |
|---|---|---|
[data-state=open] | open | Content mounted + animated in; the trigger reflects aria-expanded. |
[data-highlighted] | item-highlighted | Keyboard/pointer-focused item. |
[data-state=checked] | item-checked | A checkbox/radio item that's on. |
[data-disabled] | item-disabled | Non-interactive item (dimmed, skipped by the keyboard). |
| Property | Token | Tier |
|---|---|---|
itemMinHeight | --garn-control-h-md | semantic · air |
When to use
Offer a list of commands or toggles from a button — row/overflow (kebab) actions, an account menu, view options.
Reach for something else when
- Selecting a value in a form (use select)
- a few always-visible options (use radio-group / a button group)
- contextual rich content or a form (use popover)
- a right-click menu (use context-menu).
Overview
DropdownMenu offers a list of commands or toggles from a button — row/overflow (kebab) actions, an account menu, view options. It's a rich compound: action DropdownMenuItems, toggleable CheckboxItems, single-select RadioGroup/RadioItems, Label/Separator for structure, Shortcut hints, and nested Sub menus. It's a menu of actions, not a form control — to pick a value that stays on a field use Select, to right-click an element use ContextMenu, and to search-and-run use Command.
Guidelines
Menus are for actions, not data entry. Use it to do things or flip view toggles; if the user is choosing a form value, that's a Select. Order items by likelihood, group related ones with a Separator, and keep destructive actions visually distinct and away from the default-focus path.
Label icon-only triggers and keep the list shallow. The trigger needs an accessible name (a kebab button is meaningless to screen readers otherwise). Avoid deep Sub nesting — one level is usually plenty; more becomes a maze on touch.
Shortcut is a hint, not a binding. It displays the key combo but doesn't register it — wire the actual keyboard handler yourself. Selecting an item closes the menu; for toggles that should persist the menu open, use CheckboxItem/RadioItem.
Best practices
- Give the trigger an accessible name (aria-label if it's icon-only).
- Group with DropdownMenuLabel + DropdownMenuSeparator; keep destructive items separated.
- Control CheckboxItem (
checked+onCheckedChange) and RadioGroup (value+onValueChange) state.
- Don't use a dropdown-menu as a form select — use select.
- Don't rely on DropdownMenuShortcut to actually bind a key — it's display only; wire the handler yourself.
- Don't nest deep submenu trees — flatten or split the menu.
Content guidelines
- Phrase items as actions (verb-first) with short, parallel labels.
- Group related items, separate destructive ones, and show shortcuts as hints — not instructions.
Troubleshooting
A checkbox item closes the menu every time it's toggled.
Cause. Selecting an item closes the menu by default.
Fix. Call e.preventDefault() in the item's onSelect to keep the menu open for multi-toggle.
The toggle/radio state resets each time the menu reopens.
Cause. CheckboxItem/RadioGroup state wasn't lifted.
Fix. Control checked + onCheckedChange (checkbox) or value + onValueChange (radio group) in your state.
An icon-only trigger is announced with no name.
Cause. The menu doesn't name its trigger.
Fix. Pass aria-label to the DropdownMenuTrigger (or the button it wraps).
Accessibility
- Role
menu- ARIA APG
- menu button
- Focus
- Roving focus across items; focus returns to the trigger on close.
Accessibility requirements
Give an icon-only menu trigger an accessible name (aria-label).
when the DropdownMenuTrigger is an icon-only control with no aria-label / aria-labelledby / text
Keyboard
| EnterSpaceArrowDownArrowUp | Open the menu from the trigger. |
| ArrowUpArrowDown | Move between items. |
| ArrowRightArrowLeft | Open / close a submenu. |
| HomeEnd | Jump to the first / last item. |
| A–Z | Type-ahead to a matching item. |
| EnterSpace | Activate the highlighted item. |
| Escape | Close the menu and return focus to the trigger. |
- Trigger is a menu button (aria-haspopup=menu, aria-expanded); Content is role=menu with roving focus.
- Items are menuitem / menuitemcheckbox / menuitemradio; checked items expose aria-checked.
- An icon-only trigger relies on the menu for nothing — give the trigger its own accessible name.