Dropdown Menu
A Dropdown Menu allows users to choose an action from a list that opens from a button.
When to Use
- for the actions on one record, such as “Open”, “Send reminder”, and “Void” on a row of invoices
- for the account actions at the foot of a Sidebar
- to change settings that apply immediately, with checkbox items, like the columns that a table shows
- to choose one option among a few, with radio items, like the sort order of a table
When Not to Use
- to choose a value for a form, as a menu runs actions and has no value (use a Select)
- for commands numerous enough to need a search (use Command)
- to repeat the actions of a record on a right click (add a Context Menu)
- for the menus of an editor, side by side (use a Menubar)
- for links to the sections of a site (use a Navigation Menu)
| Dropdown Menu | Context Menu | Menubar | Navigation Menu | |
|---|---|---|---|---|
| Purpose | Actions on one record or control | The same actions, at the pointer | An editor’s commands, in menus | Links to the sections of a site |
| Opening | A click on a button | A right click or a long press | A click on a name in the bar | The pointer over a trigger, or a click |
| Position | Below the button | At the pointer | Below the name | Below the trigger |
| Content | Menu items | Menu items | Menu items | Links, in any layout |
| Page Behind | Locked | Locked | Locked | Interactive |
| Role | menu | menu | menubar, with a menu per name | None. The links are in a nav element. |
Anatomy
| Element | Usage |
|---|---|
| Trigger | Required |
| Group label | Optional |
| Item | Required |
| Shortcut | Optional |
| Popup | Required |
| Submenu trigger | Optional |
| Separator | Optional |
| Checkbox item | Optional |
| Check | On a checked item |
| Radio item | Optional |
DropdownMenuContent renders the portal, the positioner, and the popup, so you don’t need to add them. DropdownMenuSubTrigger adds the caret, and a checkbox or a radio item adds the check.
Item Types
A Context Menu and a Menubar have the same parts under their own prefixes, such as ContextMenuItem.
| Part | Purpose | Click |
|---|---|---|
DropdownMenuItem | An action | Runs it and closes the menu |
DropdownMenuCheckboxItem | A setting that is on or off | Changes it. The menu stays open. |
DropdownMenuRadioItem | One option of a DropdownMenuRadioGroup | Selects it. The menu stays open. |
DropdownMenuSubTrigger | The name of a submenu | Opens the submenu |
DropdownMenuLabel | The name of a group | None. It can’t receive focus. |
DropdownMenuSeparator | A line between groups | None |
DropdownMenuShortcut | The keys that run an item, as text at its end | None. It doesn’t bind a key. |
DropdownMenuShortcut only shows the keys, so listen for them in your application. To show a key outside a menu, use a Kbd.
If you want to change whether a click on an item closes the menu, set closeOnClick on that item.
To align an item, a label, or a submenu trigger with the text of an item that has an icon, set inset.
Variants
Set variant on DropdownMenuItem.
| Variant | Purpose |
|---|---|
default | Any action |
destructive | An action that deletes or cancels a record, such as “Void” on an invoice |
Place a destructive item last and below a separator so that it is set apart from the other actions.
Behavior
By default, a Dropdown Menu is modal: while it is open, Base UI locks the page’s scroll and prevents clicks from reaching the page. If the menu is opened by touch, the page can still scroll. If you want the page to stay interactive, set modal={false} on DropdownMenu.
Position
By default, the menu opens below the trigger and is aligned with its start edge. If the menu doesn’t fit there, Base UI moves it to another side. To change its position, use side, sideOffset, align, and alignOffset on DropdownMenuContent. For example, set align="end" for the menu at the end of a table row. A submenu opens at the end edge of its trigger.
Size and Overflow
By default, the menu is as wide as its trigger, with a minimum width of 128px, and long items will wrap. We recommend setting a width on DropdownMenuContent, for example w-48. If the menu is taller than the space available for it, it scrolls inside the popup.
Content
| String | Rule | Example | Counterexample |
|---|---|---|---|
| Item | Sentence case | “Send reminder” | “Send Reminder” |
| Item | Start an item that runs an action with a verb | “Send reminder” | “Reminder” |
| Label | Say what the group’s items share | “Sort by” | “Options” |
Accessibility
Base UI’s Menu sets the roles, moves focus into the menu, and returns it to the trigger. You provide the name of the trigger.
Keyboard
| Key | Result |
|---|---|
Enter, Space, or ArrowDown on the trigger | Opens the menu. Focus moves to the first item. |
ArrowUp on the trigger | Opens the menu. Focus moves to the last item. |
ArrowDown, ArrowUp | Moves focus to the next or the previous item, including disabled items. From the last item, focus moves to the first. |
Home, End | Moves focus to the first or the last item |
| A letter | Moves focus to the next item that starts with it |
Enter or Space on an item | Runs it and closes the menu. Focus returns to the trigger. A disabled item does nothing. |
Enter or Space on a checkbox or a radio item | Changes it. The menu stays open. |
ArrowRight, Enter, or Space on a submenu trigger | Opens the submenu. Focus moves to its first item. |
ArrowLeft in a submenu | Closes the submenu. Focus returns to its trigger. |
Escape | Closes the menu and returns focus to the trigger. In a submenu, closes only the submenu. |
Tab | Closes the menu. Focus moves to the control after the trigger. |
Shift+Tab | Closes the menu. Focus returns to the trigger. |
Requirements
An icon-only trigger has no text, so it needs an aria-label for screen readers to announce it, as
WCAG 2.2 SC 4.1.2 Name, Role, Value requires. The menu is named by its trigger, so one label names both. If the trigger repeats in a
list, name the record, for example “Actions for INV-2026-014”.
Place a DropdownMenuLabel inside a DropdownMenuGroup or a DropdownMenuRadioGroup. If a label is outside a group, Base UI throws an error.
Set aria-hidden on an icon in an item. See Iconography for the rule.
Installation
npx shadcn@latest add @summit/dropdown-menuUsage
To run an action from an item, pass it to onClick. DropdownMenuTrigger has no style of its own, so pass it a Button through render.
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>Account</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuItem onClick={openProfile}>Profile</DropdownMenuItem>
<DropdownMenuItem onClick={openBilling}>Billing</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive" onClick={signOut}>
Sign out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>;On a Table Row
For the menu of a table row, pass the trigger an icon-only Button that names the record, and set align="end" so that the end edge of the menu is aligned with the end edge of the Button.
import { DotsThreeIcon } from '@phosphor-icons/react';
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="ghost" size="icon-sm" aria-label="Actions for INV-2026-014" />}>
<DotsThreeIcon weight="bold" />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuItem>Open</DropdownMenuItem>
<DropdownMenuItem>Send reminder</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">Void</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>;With Checkbox Items
To keep the settings in the parent, pass checked and onCheckedChange to the checkbox items.
import { useState } from 'react';
const [columns, setColumns] = useState({ client: true, project: false });
<DropdownMenuContent className="w-48">
<DropdownMenuGroup>
<DropdownMenuLabel>Show columns</DropdownMenuLabel>
<DropdownMenuCheckboxItem
checked={columns.client}
onCheckedChange={(client) => setColumns({ ...columns, client })}
>
Client
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem
checked={columns.project}
onCheckedChange={(project) => setColumns({ ...columns, project })}
>
Project
</DropdownMenuCheckboxItem>
</DropdownMenuGroup>
</DropdownMenuContent>;With Radio Items
To keep the choice in the parent, pass value and onValueChange to DropdownMenuRadioGroup.
import { useState } from 'react';
const [sort, setSort] = useState('due');
<DropdownMenuContent className="w-48">
<DropdownMenuGroup>
<DropdownMenuLabel>Sort by</DropdownMenuLabel>
<DropdownMenuRadioGroup value={sort} onValueChange={setSort}>
<DropdownMenuRadioItem value="due">Due date</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="amount">Amount</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
</DropdownMenuContent>;With an Icon and a Shortcut
Place the icon first and the DropdownMenuShortcut last.
import { CopyIcon } from '@phosphor-icons/react';
<DropdownMenuItem onClick={duplicate}>
<CopyIcon aria-hidden />
Duplicate
<DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
</DropdownMenuItem>;Links as Items
If an item navigates to another page, pass an a to render on DropdownMenuItem. The item keeps role="menuitem", and a click or Enter follows the link.
<DropdownMenuItem render={<a href="/settings/billing" />}>Billing</DropdownMenuItem>Open a Dialog From an Item
To open a Dialog from an item, keep the Dialog’s open state in the parent and set it in the item’s onClick. Place the Dialog next to the menu, not inside it. See the Base UI Menu documentation for this pattern.
import { useState } from 'react';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
const [renaming, setRenaming] = useState(false);
<>
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>Project</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuItem onClick={() => setRenaming(true)}>Rename</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Dialog open={renaming} onOpenChange={setRenaming}>
<DialogContent>
<DialogTitle>Rename project</DialogTitle>
</DialogContent>
</Dialog>
</>;API Reference
DropdownMenuShortcut accepts the props of a span. The other parts accept the props of their matching parts in Base UI’s Menu, where DropdownMenuContent is Menu.Popup, DropdownMenuLabel is Menu.GroupLabel, and DropdownMenuSub is Menu.SubmenuRoot. See the Base UI Menu documentation for open, onOpenChange, modal, closeOnClick, checked, onCheckedChange, value, onValueChange, and OffsetFunction.
DropdownMenuContent
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'top' | 'bottom' | 'left' | 'right' | 'inline-start' | 'inline-end' | 'bottom' | The side of the trigger the menu opens on |
sideOffset | number | OffsetFunction | 4 | The gap between the trigger and the menu, in pixels |
align | 'start' | 'center' | 'end' | 'start' | The edge of the trigger the menu is aligned with |
alignOffset | number | OffsetFunction | 0 | A shift along that edge, in pixels |
DropdownMenuSubContent accepts the same props. Its defaults are 'inline-end', 0, 'start', and -3.
DropdownMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'destructive' | 'default' | The color of the text and of the highlight |
inset | boolean | false | Aligns the item with the text of an item that has an icon |
DropdownMenuLabel, DropdownMenuSubTrigger, DropdownMenuCheckboxItem, and DropdownMenuRadioItem also accept inset.