Skip to content
garn

Dropdown Menu

A menu of actions and toggles launched from a button.

Stablev0.4.2added in v0.1.0@garn/ui/dropdown-menu
On this page

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.

terminal
npx garn-ui add dropdown-menu
First time? Set up garn in your project
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.

DropdownMenuRequired

Root — 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).

DropdownMenuRadioGroup

Groups 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).

DropdownMenuGroup

Semantic grouping of related items.

DropdownMenuSub

A 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

SelectorState
[data-state=open]open
[data-highlighted]item-highlighted
[data-state=checked]item-checked
[data-disabled]item-disabled
PropertyTokenTier
itemMinHeight--garn-control-h-mdsemantic · 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

Do
  • 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
  • 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

warn

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

EnterSpaceArrowDownArrowUpOpen the menu from the trigger.
ArrowUpArrowDownMove between items.
ArrowRightArrowLeftOpen / close a submenu.
HomeEndJump to the first / last item.
A–ZType-ahead to a matching item.
EnterSpaceActivate the highlighted item.
EscapeClose the menu and return focus to the trigger.
A1.3.1Info and RelationshipsA2.1.1KeyboardA4.1.2Name, Role, ValueAA2.4.7Focus Visible
  • 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.