import { CreditCard, LogOut, Settings, User, UserPlus, Users } from "lucide-react"
import { Button } from "@hel/ui/button"When to use it#
Use a dropdown menu for a list of actions that would clutter the page if they were all visible: the account menu in a header, the actions on a table row, a "more" button. Each item does something when it is picked, and the menu closes.
A menu is for actions. To pick a value for a form, use a Select. For a panel with controls or longer content, use a Popover. When a list of menus runs along the top of an app, use a Menubar.
Import#
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuList,
DropdownMenuItem,
DropdownMenuGroup,
DropdownMenuGroupLabel,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
} from "@hel/ui/dropdown-menu"Anatomy#
DropdownMenu holds the open state. DropdownMenuTrigger is what opens it, and DropdownMenuContent is the popup. Inside the popup, put the items in a DropdownMenuList.
The trigger renders its own button. To use a Button or an icon button of your own, pass it with render, so the two stay one element and nothing is nested.
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline">My account</Button>} />
<DropdownMenuContent>
<DropdownMenuList>
<DropdownMenuItem icon={User}>Profile</DropdownMenuItem>
</DropdownMenuList>
</DropdownMenuContent>
</DropdownMenu>A DropdownMenuItem takes an icon, and a kbd hint shown at the end. variant="destructive" makes the item red, for actions like logging out or deleting. A disabled item stays visible but cannot be picked. Use DropdownMenuGroup with a DropdownMenuGroupLabel to put a heading over part of the list, and DropdownMenuSeparator to draw a line.
The arrow keys move between items, Enter picks one, Escape closes the menu, and typing a letter jumps to an item that starts with it.
Examples#
Actions on a row#
An icon button is the usual trigger for the actions on a row of a table or a list. Give it an aria-label that says which row it is for. align="end" lines the popup up with the right edge of the button.
INV-2041
import { Copy, Download, MoreHorizontal, Pencil, Trash2 } from "lucide-react"
import {Checkboxes and radios#
DropdownMenuCheckboxItem is a setting that is on or off, and DropdownMenuRadioGroup with DropdownMenuRadioItem picks one of several. The menu stays open when one is chosen, so people can change a few settings in one visit. Set closeOnClick on an item to close it after the choice instead.
A radio item can use indicator="check" to show a tick at the end of the row, which suits an account switcher. It also takes leading, such as an avatar, and a description for a second line.
import * as React from "react"
import { UserPlus } from "lucide-react"
Searching a long menu#
For a menu with many actions, wrap it in DropdownMenuFilterProvider and add a DropdownMenuInput. Typing narrows the list, and the arrow keys move between the matches. DropdownMenuClear empties the search, and DropdownMenuEmpty shows its text when nothing matches.
import { Calculator, Calendar, Search, Settings, Smile, User } from "lucide-react"
import { Button } from "@hel/ui/button"Submenus#
Put a DropdownMenuSub inside the list. It holds a DropdownMenuSubTrigger, the row that opens it, and a DropdownMenuContent with its own list. Open it to the side with side="right", and align="start" lines it up with its row. See the first example.
Width and spacing#
width on the content has two settings. "anchor" makes the popup at least as wide as its trigger, and "fit" makes it as wide as its items, but never narrower than 10rem. To set an exact width, use className, such as w-60, as the examples do. padding takes a step of the spacing scale, and defaults to 2xs.
Shortcuts#
The kbd hint is only a label. To make a shortcut that works while the menu is open, give the item a hotkey such as "Mod+D". It shows the shortcut as the hint too. Prefer combinations with Mod or Ctrl, since single letters compete with typing to jump to an item.
Props#
DropdownMenuContent#
| Prop | Type | Default |
|---|---|---|
side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "start" |
width | "anchor" | "fit" | none |
sideOffset | number | none |
padding | a spacing step | "2xs" |
disableAnimation | boolean | false |
DropdownMenuItem#
| Prop | Type | Default |
|---|---|---|
icon | LucideIcon | none |
kbd | ReactNode | none |
hotkey | string | none |
variant | "default" | "destructive" | "default" |
disabled | boolean | false |
onClick | () => void | none |
DropdownMenuCheckboxItem and DropdownMenuRadioItem#
| Prop | Type | Default |
|---|---|---|
checked / defaultChecked / onCheckedChange | boolean | CheckboxItem only |
value | string | RadioItem only |
closeOnClick | boolean | false |
indicator | "radio" | "check" | "radio" (RadioItem only) |
leading / description | ReactNode | RadioItem only |
The menu also takes every prop of the Base UI menu, such as open and onOpenChange to control it.