Dropdown Menu
A button that opens a menu of actions and options.
Usage
Unlike a plain <button>, the trigger is wired to the menu through render — pass it your own Button (or any element) and the menu manages its aria-expanded, focus, and open/close. Every part lives in its own import under @appica/ui-react/dropdown-menu.
DropdownMenu is a compound component built on Base UI's Menu. The root holds the open state and the shared size; the parts compose the trigger and the portalled popup:
DropdownMenuTrigger— the button that toggles the menu. Userenderto project it onto your ownButton(or any element).DropdownMenuContent— the portalled, auto-positioned popup. Accepts the shared floating props (side,align,sideOffset, …).DropdownMenuItem— one action. FiresonClickand closes the menu by default.DropdownMenuGroup/DropdownMenuGroupLabel/DropdownMenuSeparator— optional structure for sectioned lists.DropdownMenuCheckboxItem/DropdownMenuRadioGroup+DropdownMenuRadioItem— stateful options with a check indicator.DropdownMenuLinkItem— an item that renders an<a>for navigation.DropdownMenuSub/DropdownMenuSubTrigger/DropdownMenuSubContent— a nested submenu.
Put a leading icon on data-icon="start" so the item reserves the right padding; push a trailing badge, Kbd shortcut, or icon to the far edge with ms-auto.
Reach for a DropdownMenu for actions triggered from a button. To pick one value from a list of options, use a Select; for the application command bar across the top of a window, use a Menubar; to open the same menu on right-click, use a Context Menu.
Examples
Account menu
A complete profile menu: a header with an Avatar, name, and a role Badge; grouped sections with count badges; a Settings submenu; an inline dark-mode Switch; and a sign-out action. The header is plain markup — only the rows that should be focusable are DropdownMenuItems.
Icons, shortcuts & animated chevron
Give items a leading icon with data-icon="start", and align a trailing Kbd shortcut with ms-auto. The trigger carries an animated chevron: mark the trigger group/trigger and add group-data-popup-open/trigger:rotate-180 to the icon so it flips while the menu is open. Paint a destructive action by overriding the item's text and highlight colors.
Groups & labels
Wrap related items in DropdownMenuGroup with a DropdownMenuGroupLabel, and split sections with DropdownMenuSeparator.
Scrollable
For a long list, cap the height and wrap the items in a ScrollArea for the custom scrollbar. Pull the ScrollArea out to the popup edges with -mx-2 (cancelling the popup's inner padding) and restore the item padding on the viewport (viewportProps={{ className: 'px-2' }}), so the scrollbar sits a hair inside the popup edge.
Checkbox items (controlled)
DropdownMenuCheckboxItem toggles a boolean and shows a check when on. Drive it with checked + onCheckedChange (or leave it uncontrolled with defaultChecked); checkbox items keep the menu open so several can be toggled in one pass. A classic real-world use is column visibility — each item controls whether a Table column is shown.
Radio items
Wrap mutually exclusive options in a DropdownMenuRadioGroup (controlled with value + onValueChange, or uncontrolled with defaultValue); each DropdownMenuRadioItem carries the value it selects. Like checkbox items, radio items keep the menu open after a choice.
Submenus
Nest a DropdownMenuSub with its own DropdownMenuSubTrigger and DropdownMenuSubContent. The trigger shows a chevron and opens on hover or with the arrow keys; the submenu opens to the side and flips automatically near a screen edge.
Open on hover
Set openOnHover on the DropdownMenuTrigger to open the menu when the pointer rests on it (it still opens on click and keyboard too). Tune the timing with delay / closeDelay. Use this sparingly — hover menus aren't reachable on touch and can feel twitchy.
Link items
DropdownMenuLinkItem renders a real <a>, so it navigates (and supports target, modifier-clicks, and right-click "open in new tab") while staying keyboard-reachable inside the menu. For internal routes, project it onto your router's link via render — here a Next.js Link (render={<Link href="…" />}) for client-side navigation; for external links, pass a plain href with target="_blank".
Sizes
size on the root scales the popup radius, item padding, and icon size together — sm, md (default), or lg.
Side & alignment
Position the popup with side (top / right / bottom / left) and align (start / center / end) on DropdownMenuContent. It still flips and shifts automatically to stay in view.
By default a menu only flips to the opposite same-axis side (e.g. right → left); it won't re-place onto the other axis, since menus cap their height and scroll instead. For a side-opening (left / right) menu, pass collisionAvoidance={{ fallbackAxisSide: 'end' }} so it can fall back to the top/bottom axis when neither side fits — that's why the left and right triggers below set it.
RTL
Every Appica UI component supports right-to-left layouts out of the box. Set the dir attribute on a container (commonly your <html> element) so CSS logical properties resolve correctly, and wrap your tree in DirectionProvider so direction-aware behavior — roving focus, popup placement, and submenu direction — follows the same direction.
The popup anchors from the trigger's leading edge, submenus open toward the inline-start side, and the submenu chevron mirrors. For setup details and caveats, see the RTL guide.
Scoped RTL needs the direction on the positioner
The popup portals to <body>, so its start/end alignment mirrors from the document direction (read from CSS,
not from DirectionProvider) — automatic when <html dir="rtl">. If you instead scope RTL to a subtree of an LTR page,
set it on the positioner so alignment still mirrors: <DropdownMenuContent positionerProps={{ dir: 'rtl' }}>. See
Floating popups in a scoped RTL region.
API reference
DropdownMenu wraps Base UI's Menu. Each part forwards every remaining prop to the matching Base UI primitive, so anything it accepts works here — the tables below list the Appica additions and the most common props. See the Base UI docs for the complete API.
DropdownMenu
The root. Holds the open state and the shared size; renders no DOM of its own.
DropdownMenuTrigger
The button that toggles the menu. Forwards Base UI's Menu.Trigger; use render to project it onto your own element.
DropdownMenuContent
The portalled, auto-positioned popup wrapping the list. Defaults to align="start" and sideOffset={6}, and accepts the shared floating props.
DropdownMenuItem
One action in the menu.
DropdownMenuCheckboxItem
A toggleable item with a check indicator. Keeps the menu open on click.
DropdownMenuRadioGroup
Wraps a set of mutually exclusive DropdownMenuRadioItems.
DropdownMenuRadioItem
One option in a DropdownMenuRadioGroup. Keeps the menu open on click.
DropdownMenuLinkItem
An item that renders an <a> for navigation. Forwards every native anchor prop (href, target, rel, …).
DropdownMenuSub
Wraps a nested submenu. Forwards Base UI's Menu.SubmenuRoot.
DropdownMenuSubTrigger
The item that opens a submenu; renders a trailing chevron. Submenus open on hover by default.
DropdownMenuSubContent
The submenu popup. Defaults to opening on the inline-end side; otherwise identical to DropdownMenuContent.
DropdownMenuGroup
Wraps one labelled section of items so a DropdownMenuGroupLabel can name it.
DropdownMenuGroupLabel
The visible heading for a DropdownMenuGroup. Size-aware; must be rendered inside a DropdownMenuGroup (or DropdownMenuRadioGroup).
DropdownMenuSeparator
A thin divider between items or groups.
Accessibility
- The trigger is a
buttonwitharia-haspopup="menu"andaria-expanded; the popup hasrole="menu"and items aremenuitem,menuitemcheckbox, ormenuitemradio. - Enter / Space / ↓ open the menu, ↑/↓ move the highlight, → opens a submenu, ← closes it, Enter selects, Escape closes, and typing jumps to the matching item (typeahead).
- Focus is trapped inside the open popup when
modal(the default) and returns to the trigger on close. disableditems are skipped by keyboard navigation and typeahead.DropdownMenuLinkItemkeeps native anchor semantics, so links are announced and openable in a new tab.- Popup enter/exit transitions are skipped when the user prefers reduced motion.