Skip to main content

dropdown menu

Displays a menu to the user - such as a set of actions or functions - triggered by a button.

Philosophy

Dropdown menus are the Swiss Army knife of action UIs. They group related actions behind a single trigger, keeping interfaces clean while maintaining discoverability. Our implementation builds on @gentleduck/primitives/dropdown-menu which wraps the base Menu primitive, adding checkbox items, radio groups, sub-menus, and keyboard navigation. The wrapper adds design-system styling while the primitive handles all interaction logic.

How It's Built

Loading diagram...

Installation


npx @gentleduck/cli add dropdown-menu

npx @gentleduck/cli add dropdown-menu

Usage

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
<DropdownMenu>
  <DropdownMenuTrigger>Open</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuLabel>My Account</DropdownMenuLabel>
    <DropdownMenuSeparator />
    <DropdownMenuItem>Profile</DropdownMenuItem>
    <DropdownMenuItem>Billing</DropdownMenuItem>
    <DropdownMenuItem>Team</DropdownMenuItem>
    <DropdownMenuItem>Subscription</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
  <DropdownMenuTrigger>Open</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuLabel>My Account</DropdownMenuLabel>
    <DropdownMenuSeparator />
    <DropdownMenuItem>Profile</DropdownMenuItem>
    <DropdownMenuItem>Billing</DropdownMenuItem>
    <DropdownMenuItem>Team</DropdownMenuItem>
    <DropdownMenuItem>Subscription</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

Examples

Checkboxes

Radio Group

Active Trigger on Open

Style the trigger to appear active while the menu is open using data-[state=open]:

Component Composition

Loading diagram...

RTL Support

Set dir="rtl" on DropdownMenu for a local override, or set DirectionProvider once at app/root level for global direction.

Motion

Use MotionDropdownMenu and MotionDropdownMenuContent for smooth enter/exit animations powered by motion. For animated sub-menus, use MotionDropdownMenuSub and MotionDropdownMenuSubContent.

API Reference

The root component that manages open/closed state and provides context to all children.

PropTypeDefaultDescription
openboolean-Controlled open state
defaultOpenbooleanfalseInitial open state (uncontrolled)
onOpenChange(open: boolean) => void-Callback when open state changes
dir'ltr' | 'rtl'-Text direction. Resolved by primitives useDirection (dir prop -> DirectionProvider -> 'ltr').
modalbooleantrueWhen true, interaction with outside elements is disabled and only menu content is visible to screen readers

Button that toggles the dropdown menu. Renders a <button> with aria-haspopup="menu".

PropTypeDefaultDescription
asChildboolean-Render as the child element instead of a <button>
disabledbooleanfalseDisables the trigger

Sets aria-expanded, aria-controls, and data-state automatically.

The dropdown content area. Handles positioning, focus management, keyboard navigation, and dismiss behavior. Automatically wrapped in a Portal.

PropTypeDefaultDescription
side'top' | 'right' | 'bottom' | 'left''bottom'Preferred side relative to the trigger
sideOffsetnumber4Main-axis offset from trigger
align'start' | 'center' | 'end''start'Cross-axis alignment
alignOffsetnumber-Cross-axis offset
avoidCollisionsbooleantrueFlip to avoid viewport overflow
collisionPaddingnumber-Padding from viewport edges
classNamestring-Additional CSS class names

Exposes data-state="open" / data-state="closed" and data-side for CSS animation.

When using popper positioning, the following CSS custom properties are available:

  • --gentleduck-dropdown-menu-content-transform-origin
  • --gentleduck-dropdown-menu-content-available-width
  • --gentleduck-dropdown-menu-content-available-height
  • --gentleduck-dropdown-menu-trigger-width
  • --gentleduck-dropdown-menu-trigger-height

Groups related items together. Renders a <div> with role="group".

PropTypeDefaultDescription
classNamestring-Additional CSS class names

A non-interactive label for grouping menu items.

PropTypeDefaultDescription
insetboolean-Adds start padding to align with items that have icons
classNamestring-Additional CSS class names

An individual menu action item. Renders a <div> with role="menuitem".

PropTypeDefaultDescription
variant'default' | 'destructive''default'Style variant for the item
insetboolean-Adds start padding to align with items that have icons
disabledboolean-Prevents interaction and styles the item as disabled
onSelect(event: Event) => void-Called when the item is selected via click or keyboard
textValuestring-Text override for typeahead search
classNamestring-Additional CSS class names

Exposes data-highlighted when focused and data-disabled when disabled.

A menu item with a toggleable checkbox indicator.

PropTypeDefaultDescription
checkedboolean | 'indeterminate'-Controlled checked state
onCheckedChange(checked: boolean) => void-Callback when checked state changes
disabledboolean-Prevents interaction
classNamestring-Additional CSS class names

Groups radio items together for single-selection behavior.

PropTypeDefaultDescription
valuestring-The currently selected radio value
onValueChange(value: string) => void-Callback when the selected value changes

A radio-selectable menu item. Must be used inside DropdownMenuRadioGroup.

PropTypeDefaultDescription
valuestring(required)The value representing this radio item
disabledboolean-Prevents interaction
classNamestring-Additional CSS class names

A visual divider between groups of menu items. Renders a styled <div>.

PropTypeDefaultDescription
classNamestring-Additional CSS class names

Displays a keyboard shortcut hint next to a menu item.

PropTypeDefaultDescription
classNamestring-Additional CSS class names

Wrapper for a submenu. Manages nested open state.

PropTypeDefaultDescription
openboolean-Controlled open state
defaultOpenbooleanfalseInitial open state (uncontrolled)
onOpenChange(open: boolean) => void-Callback when open state changes

Trigger element for a submenu. Displays a chevron icon indicating a nested menu.

PropTypeDefaultDescription
insetboolean-Adds start padding to align with inset menu items
disabledboolean-Disables the trigger
classNamestring-Additional CSS class names

Content container for a submenu. Positioned to the side of the sub-trigger.

PropTypeDefaultDescription
sideOffsetnumber-Main-axis offset from trigger
alignOffsetnumber-Cross-axis offset
classNamestring-Additional CSS class names

Exposes the same --gentleduck-dropdown-menu-* CSS custom properties as DropdownMenuContent.

MotionDropdownMenu

Same props as DropdownMenu. Wraps with useMotionRoot for exit animation support. Requires the motion package.

MotionDropdownMenuContent

Same props as DropdownMenuContent. Adds scale, blur, and opacity enter/exit animation with springBouncy transition. Requires the motion package.

MotionDropdownMenuSub

Same props as DropdownMenuSub. Wraps sub-menu with useMotionRoot for exit animation support. Requires the motion package.

MotionDropdownMenuSubContent

Same props as DropdownMenuSubContent. Adds scale and blur enter/exit animation in a Portal. Requires the motion package.