Skip to content

Design system

Documentation

DesignComponents / DropdownMenu

Components / DropdownMenu

Stable

DropdownMenu

Use DropdownMenu for a focused list of actions or choices that opens from a trigger and closes when the decision is made.

On this page

Examples

When to use

Use DropdownMenu for a short, focused list of actions or settings that belongs to one trigger. Use a select for a form value, tabs for peer views that should remain visible, and a dialog when the task needs explanation or several fields.

Good fit

View options, export actions, or a compact set of related commands.

Use another pattern

Do not hide essential navigation, long forms, or a single obvious action in a menu.

Usage

The live preview and this copied example use the same group, checkbox, radio, submenu, disabled item, and state model:

dropdown-menu-basic.tsx
"use client";

import { ChevronDown } from "@phuctech/ui/icons";
import { Button } from "@phuctech/ui/components/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@phuctech/ui/components/dropdown-menu";

export function Example() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger
        render={
          <Button variant="outline">
            View options
            <ChevronDown aria-hidden="true" />
          </Button>
        }
      />
      <DropdownMenuContent align="start">
        <DropdownMenuItem>Show archived</DropdownMenuItem>
        <DropdownMenuItem>Compact density</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Composition

Start with DropdownMenu and a DropdownMenuTrigger. Render the trigger as the existing Button, then place the menu items inside DropdownMenuContent. The content wrapper portals automatically so a nearby overflow boundary cannot clip it. Use labels and separators to create small groups; reserve submenus for a second level that is genuinely related to the parent action.

  • DropdownMenuItem is for an immediate action.
  • DropdownMenuCheckboxItem is for an independent toggle.
  • DropdownMenuRadioGroup and DropdownMenuRadioItem are for one choice from a set.
  • DropdownMenuSub groups a second level without creating a separate page.
  • Use disabled for unavailable actions and a visible explanation when needed.

Variants and states

DropdownMenu does not add a visual variant or size enum: the trigger composes Button, while the menu surface uses the shared popover tokens. The meaningful states are open/closed, disabled, checkbox selection, radio selection, and nested submenu open/closed. Choose controlled state only when the surrounding feature needs to coordinate the menu.

Icons

Use a decorative PhucTech cue such as ChevronDown beside a visible trigger label and mark it aria-hidden="true". An icon-only trigger is allowed only when it has a concise accessible name and the surrounding context makes the menu action clear.

API

The package wrapper in packages/ui/src/components/dropdown-menu.tsx exposes Base UI menu primitives with PhucTech visual classes. It keeps Base UI's behavior and uses render for composition; there is no asChild prop.

Component API
PropTypeDefaultDescriptionConstraints
defaultOpenbooleanfalseInitial open state for an uncontrolled menu.Use open/onOpenChange for controlled state.
openbooleanControlled open state for the root menu.Pair with onOpenChange.
onOpenChange(open, details) => voidCalled when the menu opens or closes.Use details.reason when the feature needs to distinguish Escape, item press, or outside press.
modalbooleantrueLimits outside interaction while the menu is open.Keep the default unless the surrounding interaction requires non-modal behavior.
orientation"vertical" | "horizontal""vertical"Sets the arrow-key direction used for roving focus.Dropdown menus normally stay vertical.
renderReactElement | ComponentRenderFnnative elementBase UI composition hook used by DropdownMenuTrigger and item primitives.Use render to compose Button; do not add asChild.
align"start" | "center" | "end""start"Aligns DropdownMenuContent to its trigger.The wrapper also accepts alignOffset.
side"top" | "right" | "bottom" | "left""bottom"Preferred side for the portaled content.Base UI may flip it near a viewport edge.
sideOffsetnumber4Distance between the trigger and content.Use the shared spacing scale for custom values.
disabledbooleanfalseDisables a trigger or menu item.Keep the reason understandable without relying on color alone.

Tokens

Component tokens
TokenRoleEffective valueUsage
--color-bg-elevatedMenu surfacesemantic elevated backgroundThe portaled popup stays distinct from the page surface.
--elevation-popoverPopup depthshared popover shadowSeparates the menu from surrounding content without arbitrary screen-specific shadows.
--radius-lgPopup geometryshared large radiusAligns the popup and submenu corners with other elevated surfaces.
--focus-ring-*Item focus3px / 2px / semantic focusThe inset ring keeps keyboard focus visible inside the menu surface.
--motion-duration-fastOpen/close timing120msPopover transitions use the fast motion scale and reduced-motion override.

Accessibility

  1. Focus the trigger and press Enter or Space to open the menu.
  2. Use Arrow Down and Arrow Up to move through items; Base UI handles type-ahead.
  3. Press Enter or Space to select an item. Arrow Right opens a submenu and Arrow Left returns to its parent.
  4. Press Escape to close the menu and return focus to the trigger. Verify this after any custom close behavior.
  5. Give the trigger a visible label; do not place essential instructions only inside a hover state.

Do and don’t

Do

Keep items short, group related actions, and test portal positioning at both viewport edges.

Don’t

Put a long form, essential navigation, or a destructive action without confirmation inside a casual menu.

  • Button is the recommended trigger composition.
  • Elevation documents the surface and portal hierarchy.
  • Accessibility documents focus and keyboard review.