Dropdown Menu

A dropdown menu component for displaying contextual actions, navigation items, and options with support for nested menus, checkboxes, radio groups, and keyboard navigation.

Usage

Basic Dropdown Menu

A simple dropdown menu with action items:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
} from "@prisma-docs/ui/components/dropdown-menu";


export function BasicDropdown() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Open Menu
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>Profile</DropdownMenuItem>
        <DropdownMenuItem>Settings</DropdownMenuItem>
        <DropdownMenuItem>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

With Icons

Add icons to menu items for better visual hierarchy:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
} from "@prisma-docs/ui/components/dropdown-menu";
import { User, Settings, LogOut } from "lucide-react";

export function DropdownWithIcons() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Account
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>
          <User />
          Profile
        </DropdownMenuItem>
        <DropdownMenuItem>
          <Settings />
          Settings
        </DropdownMenuItem>
        <DropdownMenuItem>
          <LogOut />
          Logout
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

With Labels and Separators

Organize menu items with labels and separators:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
} from "@prisma-docs/ui/components/dropdown-menu";
import { User, Settings, Mail, MessageSquare, LogOut } from "lucide-react";

export function OrganizedDropdown() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Menu
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuLabel>My Account</DropdownMenuLabel>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem>
          <User />
          Profile
        </DropdownMenuItem>
        <DropdownMenuItem>
          <Settings />
          Settings
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuLabel>Communication</DropdownMenuLabel>
        </DropdownMenuGroup>
        <DropdownMenuItem>
          <Mail />
          Email
        </DropdownMenuItem>
        <DropdownMenuItem>
          <MessageSquare />
          Messages
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem>
          <LogOut />
          Logout
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

With Checkbox Items

Add checkbox items for toggleable options:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuCheckboxItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
} from "@prisma-docs/ui/components/dropdown-menu";
import { useState } from "react";

export function CheckboxDropdown() {
  const [showStatusBar, setShowStatusBar] = useState(true);
  const [showActivityBar, setShowActivityBar] = useState(false);
  const [showPanel, setShowPanel] = useState(false);

  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        View Options
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuLabel>Appearance</DropdownMenuLabel>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuCheckboxItem
          checked={showStatusBar}
          onCheckedChange={setShowStatusBar}
        >
          Status Bar
        </DropdownMenuCheckboxItem>
        <DropdownMenuCheckboxItem
          checked={showActivityBar}
          onCheckedChange={setShowActivityBar}
        >
          Activity Bar
        </DropdownMenuCheckboxItem>
        <DropdownMenuCheckboxItem
          checked={showPanel}
          onCheckedChange={setShowPanel}
        >
          Panel
        </DropdownMenuCheckboxItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

With Radio Groups

Use radio groups for mutually exclusive options:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
} from "@prisma-docs/ui/components/dropdown-menu";
import { useState } from "react";

export function RadioDropdown() {
  const [position, setPosition] = useState("bottom");

  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Panel Position
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuLabel>Position</DropdownMenuLabel>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
          <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
        </DropdownMenuRadioGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

With Nested Submenus

Create nested dropdown menus for hierarchical navigation:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSub,
  DropdownMenuSubTrigger,
  DropdownMenuSubContent,
} from "@prisma-docs/ui/components/dropdown-menu";
import { Plus, User, Mail } from "lucide-react";

export function NestedDropdown() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Actions
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>
          <User />
          Profile
        </DropdownMenuItem>
        <DropdownMenuSub>
          <DropdownMenuSubTrigger>
            <Plus />
            New
          </DropdownMenuSubTrigger>
          <DropdownMenuSubContent>
            <DropdownMenuItem>Project</DropdownMenuItem>
            <DropdownMenuItem>Repository</DropdownMenuItem>
            <DropdownMenuItem>Team</DropdownMenuItem>
          </DropdownMenuSubContent>
        </DropdownMenuSub>
        <DropdownMenuItem>
          <Mail />
          Invite
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

With Keyboard Shortcuts

Display keyboard shortcuts for quick reference:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuShortcut,
} from "@prisma-docs/ui/components/dropdown-menu";


export function ShortcutDropdown() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Edit
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>
          Undo
          <DropdownMenuShortcut>⌘Z</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          Redo
          <DropdownMenuShortcut>⌘⇧Z</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          Cut
          <DropdownMenuShortcut>⌘X</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          Copy
          <DropdownMenuShortcut>⌘C</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          Paste
          <DropdownMenuShortcut>⌘V</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

Destructive Actions

Highlight destructive actions with the variant prop:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
} from "@prisma-docs/ui/components/dropdown-menu";
import { Settings, Trash } from "lucide-react";

export function DestructiveDropdown() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Options
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>
          <Settings />
          Settings
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          <Trash />
          Delete
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

Positioning

Control the menu position with align and side props:

import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
} from "@prisma-docs/ui/components/dropdown-menu";


export function PositionedDropdown() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger className="px-3 py-1.5 text-sm font-normal border border-fd-border rounded-low bg-background-default hover:bg-background-neutral text-foreground-neutral disabled:text-foreground-neutral-weaker active:hover:font-medium active:font-medium shadow-box outline-none">
        Positioned Menu
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" side="top">
        <DropdownMenuItem>Item 1</DropdownMenuItem>
        <DropdownMenuItem>Item 2</DropdownMenuItem>
        <DropdownMenuItem>Item 3</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Live Example:

API Reference

The root component that wraps the entire dropdown menu.

  • open - Control the open state (boolean, optional)
  • onOpenChange - Callback when open state changes ((open: boolean) => void, optional)
  • defaultOpen - Default open state (boolean, optional)
  • modal - Whether to render in a modal (boolean, default: true)

The button that toggles the dropdown menu.

  • asChild - Merge props with child component (boolean, optional)
  • disabled - Disable the trigger (boolean, optional)

The container for dropdown menu items.

  • align - Alignment relative to trigger: "start" | "center" | "end" (default: "start")
  • side - Side to display menu: "top" | "right" | "bottom" | "left" (default: "bottom")
  • alignOffset - Offset in pixels from aligned position (number, default: 0)
  • sideOffset - Offset in pixels from side (number, default: 4)
  • className - Additional CSS classes (string, optional)

An individual menu item.

  • inset - Add left padding for alignment (boolean, optional)
  • variant - Visual style: "default" | "destructive" (default: "default")
  • disabled - Disable the item (boolean, optional)
  • onSelect - Callback when item is selected ((event: Event) => void, optional)
  • className - Additional CSS classes (string, optional)

A checkbox menu item.

  • checked - Checked state (boolean | "indeterminate", optional)
  • onCheckedChange - Callback when checked state changes ((checked: boolean) => void, optional)
  • disabled - Disable the item (boolean, optional)
  • className - Additional CSS classes (string, optional)

Container for radio items.

  • value - Selected value (string, optional)
  • onValueChange - Callback when value changes ((value: string) => void, optional)

A radio menu item.

  • value - The value for this item (string, required)
  • disabled - Disable the item (boolean, optional)
  • className - Additional CSS classes (string, optional)

A label for grouping menu items.

  • inset - Add left padding for alignment (boolean, optional)
  • className - Additional CSS classes (string, optional)

A visual separator between menu items.

  • className - Additional CSS classes (string, optional)

Display keyboard shortcuts.

  • className - Additional CSS classes (string, optional)

Container for nested submenus.

  • open - Control submenu open state (boolean, optional)
  • onOpenChange - Callback when submenu open state changes ((open: boolean) => void, optional)

Trigger for opening a submenu.

  • inset - Add left padding for alignment (boolean, optional)
  • disabled - Disable the trigger (boolean, optional)
  • className - Additional CSS classes (string, optional)

Container for submenu items.

  • All props from DropdownMenuContent

Features

  • ✅ Full keyboard navigation support
  • ✅ Supports nested submenus
  • ✅ Checkbox and radio group items
  • ✅ Customizable positioning (align, side, offset)
  • ✅ Keyboard shortcuts display
  • ✅ Disabled state support
  • ✅ Destructive action styling
  • ✅ Smooth animations
  • ✅ Accessible (ARIA compliant)
  • ✅ Based on Base UI Menu primitives

Best Practices

  • Use clear, action-oriented labels for menu items
  • Group related items together with labels and separators
  • Place destructive actions at the bottom, separated from other items
  • Limit menu depth to 2 levels (avoid deeply nested submenus)
  • Use icons consistently throughout the menu
  • Show keyboard shortcuts for frequently used actions
  • Use asChild prop on trigger to merge with custom buttons
  • Keep menu items concise (1-2 words when possible)
  • Use radio groups for mutually exclusive options
  • Use checkboxes for toggleable settings
  • Disable items that are temporarily unavailable rather than hiding them

Accessibility

  • Automatically manages focus
  • Supports keyboard navigation (Arrow keys, Enter, Space, Escape)
  • ARIA attributes included
  • Screen reader friendly
  • Focus trapping within open menu
  • Escape key closes menu
  • Click outside closes menu

Common Use Cases

The DropdownMenu component is perfect for:

  • User account menus - Profile, settings, logout actions
  • Context menus - Right-click actions on items
  • More actions menus - Additional options that don't fit in the main UI
  • Settings toggles - Quick access to view or preference settings
  • Navigation menus - Mobile navigation or compact menu bars
  • Filtering options - Quick filters with checkbox items
  • Selection controls - Radio groups for mutually exclusive choices
  • Command palettes - Quick actions with keyboard shortcuts

On this page