Dropdown Menu

A list of actions that opens from a button, with icons, shortcuts, submenus, checkboxes and search.

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"

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

PropTypeDefault
side"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"
align"start" | "center" | "end""start"
width"anchor" | "fit"none
sideOffsetnumbernone
paddinga spacing step"2xs"
disableAnimationbooleanfalse
PropTypeDefault
iconLucideIconnone
kbdReactNodenone
hotkeystringnone
variant"default" | "destructive""default"
disabledbooleanfalse
onClick() => voidnone
PropTypeDefault
checked / defaultChecked / onCheckedChangebooleanCheckboxItem only
valuestringRadioItem only
closeOnClickbooleanfalse
indicator"radio" | "check""radio" (RadioItem only)
leading / descriptionReactNodeRadioItem only

The menu also takes every prop of the Base UI menu, such as open and onOpenChange to control it.