v0.5

Dropdown Menu

Action menu that opens from a button, for account controls, row actions, and overflow commands.

Description

DropdownMenu is a compound popover menu built on Radix's DropdownMenu primitive. It composes DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem (with optional DropdownMenuGroup, DropdownMenuLabel, and DropdownMenuSeparator) into a portalled, keyboard-driven menu. size is set on the root and cascades through context to every item and label.

Reach for it when a button needs to fan out into a short list of commands or one-of-many switches: account menus, row actions, overflow ("...") menus on cards, sort and filter pickers, "open in" link lists. It works equally well attached to a labeled icon button, an avatar, or a plain text trigger.

Don't use it as a form control. For binding a single value to a form field, use Select. For right-click on a region, use ContextMenu. For arbitrary popover content with no menu semantics, use Popover. For passive hover hints, use Tooltip.

Installation

pnpm dlx @create-ui/cli add dropdown-menu

Anatomy

<DropdownMenu>
  <DropdownMenuTrigger />
  <DropdownMenuContent>
    <DropdownMenuGroup>
      <DropdownMenuLabel />
      <DropdownMenuItem />
      <DropdownMenuSeparator />
    </DropdownMenuGroup>
  </DropdownMenuContent>
</DropdownMenu>

Usage

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button>Open</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem>Profile</DropdownMenuItem>
    <DropdownMenuItem>Settings</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

Examples

Sizes

Three sizes (xs, sm, md) match the rest of the type and form scale. sm is the default. Set size on the root; trigger, content radius, item height, label, and icon size all scale from there.

With Icons

Drop an icon as the first child of DropdownMenuItem to render it as a leading affordance. The item handles its own gap and aligns the icon to the size scale.

Groups and Separator

Use DropdownMenuGroup to bundle related items, DropdownMenuLabel to head a group, and DropdownMenuSeparator to split sections. Groups are semantic only, the separator is what creates the visible break.

Selected

selected marks an item as the active value: text shifts to body weight and a primary-colored check is rendered at the trailing edge. Use showCheck when you want the trailing check glyph without the selected styling (e.g. for items toggleable independently).

With Avatar

Common account-menu pattern: a pill-shaped, icon-only Button holds an Avatar as the trigger, and align="end" keeps the menu pinned to the trigger's right edge.

Pass asChild on DropdownMenuItem to render its underlying element as an <a> (or a router Link). The icon, padding, and highlight styles still apply to the link child.

Accessibility

DropdownMenu inherits Radix's menu keyboard model. The trigger is a single tab stop. Once the menu opens, arrow keys move focus between items, type-ahead jumps to a matching item, and Esc returns focus to the trigger.

KeyDescription
Space EnterOpens the menu; activates the focused item.
↑ ↓Moves focus between items inside the open menu.
Home EndJumps to the first / last item.
A-ZType-ahead; focuses the first item matching the input.
EscCloses the menu and returns focus to the trigger.
TabCloses the menu and moves focus to the next element.

ARIA notes:

  • Radix wires role="menu" on the content, role="menuitem" on each item, and aria-haspopup / aria-expanded on the trigger. The portal handles focus trapping while the menu is open.
  • selected on DropdownMenuItem is a visual cue (data-selected + check icon), not a true checkbox role. For toggleable boolean items with proper semantics, use Radix's DropdownMenu.CheckboxItem or DropdownMenu.RadioItem directly.
  • Disabled items receive data-disabled and are skipped by keyboard navigation. Always give icon-only triggers an aria-label.
  • For portal, focus, and animation behavior beyond what's documented here, see Radix DropdownMenu.

Styling

Tailwind override: pass className to merge Tailwind classes on DropdownMenuContent or any sub-part (merged via cn()):

<DropdownMenuContent className="w-56" />

Data slots and attributes: the component sets these for CSS targeting:

  • data-slot="dropdown-menu" on the root, data-slot="dropdown-menu-trigger" on the trigger, data-slot="dropdown-menu-portal" on the portal, data-slot="dropdown-menu-content" on the popover, data-slot="dropdown-menu-group" on each group, data-slot="dropdown-menu-label" on labels, data-slot="dropdown-menu-item" on items, data-slot="dropdown-menu-separator" on separators.
  • data-selected on the item when the selected prop is true.
  • Radix-managed: data-state="open" | "closed" on trigger and content, data-side="top" | "right" | "bottom" | "left" and data-align="start" | "center" | "end" on content, data-highlighted and data-disabled on each item.

Target a state in CSS:

[data-slot="dropdown-menu-item"][data-highlighted] {
  /* … */
}
  • Select: form-value picker bound to a Field. Use it when the chosen value is what the form submits.
  • ContextMenu: same menu shape, triggered by right-click (or long-press) on a region.
  • Popover: arbitrary content with no menu semantics or roving focus.
  • Tooltip: passive hover description, never a place to put actions.

API Reference

Wraps Radix's DropdownMenu.Root and provides the DropdownMenuContext that sub-parts read for size. Extends React.ComponentProps<typeof DropdownMenuPrimitive.Root>, so all Radix root props (open, defaultOpen, onOpenChange, modal, dir) pass through.

Props

PropTypeDefaultDescription
openboolean-Controlled open state of the menu.
defaultOpenboolean-Uncontrolled initial open state.
onOpenChange(open: boolean) => void-Fires when the menu opens or closes.
modalbooleantrueWhen true, blocks interaction with content outside the menu.
dir"ltr" | "rtl"-Reading direction; auto-detected from the document when unset.
childrenReact.ReactNode-The trigger and content sub-parts.

Variants

VariantOptionsDefaultDescription
size"xs" "sm" "md""sm"Cascades to trigger, content radius, label, and items.

Wraps Radix's DropdownMenu.Trigger. Extends Radix DropdownMenu.Trigger props (asChild, plus any standard button attributes).

Props

PropTypeDefaultDescription
asChildbooleanfalseRender as the child element via Radix Slot. Use to make a Button the trigger.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-The trigger content (typically a Button).

Wraps Radix's DropdownMenu.Portal. Extends React.ComponentProps<typeof DropdownMenuPrimitive.Portal>. Rarely used directly because DropdownMenuContent already portals itself.

Props

PropTypeDefaultDescription
containerHTMLElementdocument.bodyThe mount node for the portal.
forceMountboolean-Force mount when controlling animations outside Radix. (advanced)
childrenReact.ReactNode-The content sub-part.

Wraps Radix's DropdownMenu.Content rendered inside DropdownMenu.Portal. Extends React.ComponentProps<typeof DropdownMenuPrimitive.Content>, so all Radix content props pass through.

Props

PropTypeDefaultDescription
align"start" | "center" | "end""start"Horizontal alignment relative to the trigger.
side"top" | "right" | "bottom" | "left"-Preferred side; Radix flips when space is tight.
sideOffsetnumber4Distance in pixels from the trigger.
alignOffsetnumber-Offset along the aligned axis.
avoidCollisionsbooleantrueReposition the menu to keep it in the viewport.
collisionPaddingnumber | Partial<Record<Side, number>>-Padding from the viewport edge when avoiding collisions.
loopboolean-When true, keyboard navigation wraps from last to first.
onCloseAutoFocus(event: Event) => void-Fires when focus returns after the menu closes. (advanced)
onEscapeKeyDown(event: KeyboardEvent) => void-Fires when Esc is pressed inside the menu.
onPointerDownOutside(event: PointerDownOutsideEvent) => void-Fires when the user clicks outside the menu.
forceMountboolean-Force mount when controlling animations outside Radix. (advanced)
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-DropdownMenuItem, DropdownMenuGroup, DropdownMenuLabel, and DropdownMenuSeparator parts.

Wraps Radix's DropdownMenu.Group. Use it to bundle related items, optionally with a DropdownMenuLabel header.

Props

PropTypeDefaultDescription
asChildbooleanfalseRender as the child element via Radix Slot.
childrenReact.ReactNode-The label and items inside the group.

Wraps Radix's DropdownMenu.Label. Sized by the surrounding DropdownMenuContext. Renders a non-interactive heading above a group of items.

Props

PropTypeDefaultDescription
asChildbooleanfalseRender as the child element via Radix Slot.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-Label text rendered above the item group.

Wraps Radix's DropdownMenu.Item. Extends React.ComponentProps<typeof DropdownMenuPrimitive.Item>, so onSelect, disabled, textValue, and asChild pass through. The first child can be an icon; the remaining children become the label.

Props

PropTypeDefaultDescription
selectedboolean-Marks the item as the active value; renders a primary-tinted check and body-weight text.
showCheckboolean-Renders the trailing check icon without the selected styling.
asChildbooleanfalseRender the item as the child element (e.g. an <a> or router Link).
disabledboolean-Disables the item; skipped by keyboard navigation.
onSelect(event: Event) => void-Fires when the item is activated. Call event.preventDefault() to keep the menu open.
textValuestring-Override the string used for type-ahead matching.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-Optional leading icon followed by the item label.

Wraps the shared Separator component. A thin divider for splitting groups inside the content.

Props

PropTypeDefaultDescription
classNamestring-Tailwind classes merged via cn().