UIUpdated April 28, 2026

Dropdown Menu

Context menu component built on Radix DropdownMenu. Supports groups, submenus, checkbox/radio items, and keyboard shortcuts.

UIDropdown Menu

Compact actions in contextLink to section

The Dropdown Menu component displays a list of actions inside a floating surface anchored to a trigger.

Use it for:

  • row actions
  • account menus
  • editor menus
  • compact command lists
  • secondary actions
  • option selectors
  • small contextual choices

Dropdown Menu is built on Radix DropdownMenu and styled with PyColors tokens such as bg-popover, border-border, and text-popover-foreground.

Core idea

Dropdown Menu is for compact contextual actions. It should not become a full workflow surface.

ImportLink to section

dropdown-menu-import.tsx
import { DropdownMenu } from "@pycolors/ui";

Basic usageLink to section

Use DropdownMenuTrigger asChild with a semantic trigger, usually a Button.

basic-dropdown-menu.tsx
import {
  Button,
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
} from "@pycolors/ui";

export function BasicDropdownMenu() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="sm">
          Open menu
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="start">
        <DropdownMenuItem>Profile</DropdownMenuItem>
        <DropdownMenuItem>Settings</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem className="text-destructive">
          Delete
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Trigger rule

Prefer asChild so the trigger keeps the semantics of the element you pass, such as a real button.

When to use Dropdown MenuLink to section

Use Dropdown Menu when the actions are secondary, contextual, or compact.

Row actions

Use for rename, duplicate, archive, export, or delete actions on one record.

Preferences

Use checkbox or radio items for compact option selection.

Account menu

Use for compact user-level navigation such as profile, settings, or sign out.

Grouping, labels, and shortcutsLink to section

Use labels and groups when a menu has more than one kind of action.

grouped-dropdown-menu.tsx
import {
  Button,
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuLabel,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
} from "@pycolors/ui";

export function GroupedDropdownMenu() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="sm">
          Actions
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="start">
        <DropdownMenuLabel>Project</DropdownMenuLabel>

        <DropdownMenuGroup>
          <DropdownMenuItem>
            Rename
            <DropdownMenuShortcut>⌘R</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            Duplicate
            <DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>

        <DropdownMenuSeparator />

        <DropdownMenuItem className="text-destructive">
          Delete
          <DropdownMenuShortcut>⌫</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Shortcut rule

DropdownMenuShortcut is visual. It does not automatically register keyboard shortcuts.

Checkbox itemsLink to section

Use checkbox items for independent toggles.

Interactive examples with state must live in a Client Component.

Loading preview…
checkbox-menu-preview.tsx
"use client";

import * as React from "react";
import {
  Button,
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuCheckboxItem,
  DropdownMenuSeparator,
} from "@pycolors/ui";

export function CheckboxMenuPreview() {
  const [enabled, setEnabled] = React.useState(true);
  const [pinned, setPinned] = React.useState(false);

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="sm">
          Options
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="start">
        <DropdownMenuCheckboxItem
          checked={enabled}
          onCheckedChange={(value) => setEnabled(Boolean(value))}
        >
          Enabled
        </DropdownMenuCheckboxItem>

        <DropdownMenuCheckboxItem
          checked={pinned}
          onCheckedChange={(value) => setPinned(Boolean(value))}
        >
          Pinned
        </DropdownMenuCheckboxItem>

        <DropdownMenuSeparator />

        <DropdownMenuCheckboxItem checked disabled>
          Disabled option
        </DropdownMenuCheckboxItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Radio groupLink to section

Use radio items for exclusive selection.

radio-menu-preview.tsx
"use client";

import * as React from "react";
import {
  Button,
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
} from "@pycolors/ui";

export function RadioMenuPreview() {
  const [theme, setTheme] = React.useState("system");

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="sm">
          Theme
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="start">
        <DropdownMenuLabel>Appearance</DropdownMenuLabel>

        <DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
          <DropdownMenuRadioItem value="system">System</DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="light">Light</DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="dark">Dark</DropdownMenuRadioItem>
        </DropdownMenuRadioGroup>

        <DropdownMenuSeparator />

        <DropdownMenuRadioItem value="contrast" disabled>
          High contrast (soon)
        </DropdownMenuRadioItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Use submenus sparingly.

They are useful when a single action has a small secondary choice, such as export format.

dropdown-submenu.tsx
import {
  Button,
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSub,
  DropdownMenuSubTrigger,
  DropdownMenuSubContent,
  DropdownMenuSeparator,
} from "@pycolors/ui";

export function DropdownSubmenu() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="sm">
          More
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="start">
        <DropdownMenuItem>Rename</DropdownMenuItem>

        <DropdownMenuSub>
          <DropdownMenuSubTrigger>
            Export
          </DropdownMenuSubTrigger>

          <DropdownMenuSubContent>
            <DropdownMenuItem>PDF</DropdownMenuItem>
            <DropdownMenuItem>CSV</DropdownMenuItem>
            <DropdownMenuItem>JSON</DropdownMenuItem>
          </DropdownMenuSubContent>
        </DropdownMenuSub>

        <DropdownMenuSeparator />

        <DropdownMenuItem className="text-destructive">
          Delete
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Submenu rule

Keep submenu depth to one level whenever possible. Deep nested menus are harder to scan and navigate.

Usage patternsLink to section

Start with the triggerLink to section

Use a clear trigger such as Actions, More, or an icon button with an accessible label.

Keep items action-orientedLink to section

Prefer verb-first labels: Rename, Duplicate, Archive, Export, Delete.

Group only when neededLink to section

Use labels and separators to clarify structure, not to decorate the menu.

Confirm destructive actionsLink to section

A destructive menu item should usually open a Dialog before the final action.

Move complex workflows elsewhereLink to section

If the menu contains forms, long content, or multi-step decisions, use Sheet, Dialog, or a page.

SituationUse
Row actionsDropdownMenu
Account menuDropdownMenu
Small preference selectorDropdownMenu
Destructive confirmationDialog
Advanced filtersSheet
Long settings workflowSheet or page
Multi-step flowPage

Decision rule

DropdownMenu exposes compact choices. Dialog confirms focused decisions. Sheet handles larger side workflows.

APILink to section

ComponentsLink to section

ComponentDescription
DropdownMenuRoot container
DropdownMenuTriggerAnchor element, usually with asChild
DropdownMenuContentFloating menu surface
DropdownMenuItemStandard action item
DropdownMenuCheckboxItemIndependent toggle item
DropdownMenuRadioGroupExclusive selection group
DropdownMenuRadioItemItem inside a radio group
DropdownMenuLabelSection label
DropdownMenuSeparatorDivider
DropdownMenuShortcutRight-aligned visual shortcut
DropdownMenuGroupGroup wrapper
DropdownMenuSubSubmenu root
DropdownMenuSubTriggerSubmenu trigger item
DropdownMenuSubContentSubmenu surface
DropdownMenuPortalPortal wrapper for menu content

Wrapper additionsLink to section

ComponentAdded propPurpose
DropdownMenuIteminset?: booleanAlign item with indicator spacing
DropdownMenuLabelinset?: booleanAlign label with inset items
DropdownMenuSubTriggerinset?: booleanAlign submenu trigger with inset items

Dropdown Menu components extend their respective Radix props.

dropdown-menu-props.ts
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>

AccessibilityLink to section

Dropdown Menu is built on Radix DropdownMenu primitives.

Keyboard support includes:

  • Enter or Space activates an item
  • Arrow keys navigate items
  • Escape closes the menu
  • Submenus support keyboard navigation
  • Focus returns to the trigger when closed

Guidelines:

  • use DropdownMenuTrigger asChild with a semantic trigger
  • provide an aria-label for icon-only triggers
  • avoid complex inputs inside dropdown menus
  • do not rely on shortcut text as the only instruction
  • confirm destructive actions in a Dialog when needed

Accessibility rule

A dropdown menu should be quick to scan and quick to leave. If users need to think, read, or type, use another surface.

Prefer / avoidLink to section

Prefer

  • short action labels
  • semantic triggers
  • groups only when they improve scanning
  • Dialog confirmation for destructive actions
  • submenus only for small secondary choices

Avoid

  • long paragraphs inside menus
  • deep nested submenus
  • forms or complex inputs in a dropdown
  • destructive actions with no confirmation
  • using menus for primary actions

Product copy guidelinesLink to section

Menu item labels should be short and action-oriented.

  • Rename
  • Duplicate
  • Archive
  • Export CSV
  • Delete
  • Click here to rename this project
  • Make another copy of this project
  • Remove forever immediately
  • Open another complicated flow

Common questionsLink to section

Next step

Ready to go further?

Move from documentation to a real production setup with authentication, billing, and backend foundations already wired.

Was this helpful?