DropdownMenu
A list of actions behind a button: Rename, Duplicate, Delete. It opens on the raised surface with items the height and look of Select's options, and moves with the arrow keys. Items can carry a leading icon and a shortcut, turn options on and off, or pick one of several.
Preview
import { IconCopy, IconPencil, IconTrash } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Usage
When to use
- Several actions on one item behind one button: Rename, Duplicate, Delete.
- Options that turn on and off, or one of several, in a toolbar or a header.
When not to use
- Picking a value in a form. Use Select.
- Fields or other controls. Use Popover.
- The main action of a screen, or the only way to do a common action. Use Button.
Variants
side
Set on DropdownMenuContent. The side of the trigger it opens on. It flips when there is no room.
import { IconCopy, IconPencil, IconTrash } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="top">
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="right">
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left">
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>align
Set on DropdownMenuContent. start lines the menu up with the trigger's start edge; end for a trigger at the end of a row.
import { IconCopy, IconPencil, IconTrash } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="center">
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Options</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem leadingIcon={<IconPencil size={16} weight="regular" />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} weight="regular" />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} weight="regular" />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Examples
Actions
import { IconCopy, IconDots, IconPencil, IconTrash } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" iconOnly aria-label="More actions">
<IconDots size={16} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem leadingIcon={<IconPencil size={16} />}>Rename</DropdownMenuItem>
<DropdownMenuItem leadingIcon={<IconCopy size={16} />} shortcut="⌘D">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem destructive leadingIcon={<IconTrash size={16} />}>Delete project</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Groups and choices
A checkbox item turns an option on or off; radio items pick one. In an app, pair checked with onCheckedChange and value with onValueChange.
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">View</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel inset>Show</DropdownMenuLabel>
<DropdownMenuCheckboxItem checked>Grid</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>Rulers</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuLabel inset>Sort by</DropdownMenuLabel>
<DropdownMenuRadioGroup value="edited">
<DropdownMenuRadioItem value="edited">Last edited</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="name">Name</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>Links
asChild makes a link the item, for actions that go to another page.
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Account</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem asChild>
<a href="#profile">Profile</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="#billing">Billing</a>
</DropdownMenuItem>
<DropdownMenuItem disabled>Team settings</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Do and don't
Do
- Start each item with a verb: Rename, Duplicate, Delete.
- Put destructive actions last, after a separator, and confirm them with an AlertDialog.
- Name an icon-only trigger with aria-label, such as More actions.
Don't
- Don't use it to pick a value in a form; use Select.
- Don't put fields or other controls in it; use Popover.
- Don't hide the only way to do a common action in a menu; give it a Button.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| open | boolean | - | - | Set on DropdownMenu. Whether it is open, when you control it. Pair it with onOpenChange. |
| defaultOpen | boolean | - | false | Set on DropdownMenu. Opens it on first render, when you don't control it. |
| onOpenChange | function | - | - | Set on DropdownMenu. Called with true or false when it opens or closes. |
| modal | boolean | - | true | Set on DropdownMenu. true keeps the rest of the page out of reach (no hover, no scroll) while the menu is open. |
| side | enum | top | right | bottom | left | bottom | Set on DropdownMenuContent. The side of the trigger it opens on. It flips when there is no room. |
| align | enum | start | center | end | start | Set on DropdownMenuContent. start lines the menu up with the trigger's start edge; end for a trigger at the end of a row. |
| sideOffset | number | - | 4 | Set on DropdownMenuContent. Space between the trigger and the menu, in px. 4 is spacing-xs. |
| loop | boolean | - | false | Set on DropdownMenuContent. The arrow keys wrap from the last item to the first. |
| destructive | boolean | - | false | Set on DropdownMenuItem. Danger text for an action that deletes or can't be undone. Confirm it with an AlertDialog. |
| disabled | boolean | - | false | Set on an item. It can't be highlighted or run, and the arrow keys skip it. |
| inset | boolean | - | false | Set on DropdownMenuItem or DropdownMenuLabel. Lines the label up with items that have an icon or a check. |
| leadingIcon | node | - | - | Set on DropdownMenuItem. A decorative 16px icon before the label: <IconPencil size={16} />. |
| shortcut | string | - | - | Set on DropdownMenuItem. The keyboard shortcut at the end: ⌘D. The app handles the key itself. |
| asChild | boolean | - | false | Set on DropdownMenuItem. Renders the child (a link) as the item, for actions that go to another page. leadingIcon and shortcut don't apply; put the icon inside the link. |
Slots
| Name | Required | Description |
|---|---|---|
| trigger | yes | DropdownMenuTrigger with asChild around a Button. |
| content | yes | DropdownMenuContent with one DropdownMenuItem per action, in the order users need them, destructive ones last. Group long menus with DropdownMenuLabel and DropdownMenuSeparator. |
Anatomy
| Part | Description |
|---|---|
| root | DropdownMenuItem: one action. The md control height (32px on mouse, 36px on touch), inner corners, 14px primary text, a leading icon in the primary icon color, the neutral hover overlay when highlighted. destructive turns the text and icon to the danger text color. |
| destructive | A destructive DropdownMenuItem (destructive): danger text on the same neutral row and highlight. Classes land on the item. |
| destructiveIcon | The leading icon of a destructive item, in the danger text color (there is no danger icon token). |
| menu | DropdownMenu. Holds the open state; renders nothing itself. |
| trigger | DropdownMenuTrigger: the button that opens the menu, usually a Button with asChild. |
| content | DropdownMenuContent: the floating list on the raised surface, at least 192px wide, the same surface as Select's list. It scrolls when it is taller than the screen minus spacing-sm at the top and bottom. |
| icon | The leading icon of an item (leadingIcon), Tabler icons, 16px. |
| indicator | The check of a DropdownMenuCheckboxItem (Tabler check, 16px) or the 6px dot of a DropdownMenuRadioItem, before the label. |
| groupLabel | DropdownMenuLabel: the heading of a group, one step smaller than an item, in secondary text. |
| separator | DropdownMenuSeparator: a line across the full menu between two groups. |
| shortcut | The shortcut at the end of an item (shortcut or DropdownMenuShortcut): 12px mono in tertiary text. |
Accessibility
Role: menu
| Keys | Action |
|---|---|
| Space, Enter, ArrowDown | On the trigger: opens the menu and focuses the first item. |
| ArrowUp, ArrowDown | Moves between items, skipping disabled ones. With loop, it wraps from the last to the first. |
| Home, End | Focuses the first or the last item. |
| Enter, Space | Runs the item and closes the menu. On a checkbox or radio item, turns it on or picks it. |
| Escape | Closes the menu and returns focus to the trigger. |
| A letter | Focuses the next item whose label starts with it. |
- The trigger gets aria-haspopup and aria-expanded; the list is a menu of menuitem, menuitemcheckbox and menuitemradio.
- Name an icon-only trigger with aria-label: More actions.
- A destructive item says what it destroys (Delete project) and opens an AlertDialog; danger text is not the only warning.
- The shortcut text is read with the label. The app handles the shortcut itself; the menu only shows it.
- A group heading (DropdownMenuLabel) is not focusable.
Install
npx shadcn add https://werewolf-ui.vercel.app/r/dropdown-menu.jsonThis also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").