A dropdown menu is a list of actions on a surface that opens from a trigger you supply. The trigger is a required prop rather than a part of the menu, so the same component serves an overflow icon, an avatar or a button. Dropdown Button is this component with the button already supplied.
Anatomy
Section titled “Anatomy”-
Trigger △ the element that opens the menu, and the element the menu positions itself against. It is a required prop rather than a part of the surface.- Record
EditDuplicateDelete- Record
EditDuplicateDelete -
Menu (when open) the elevated surface that holds the list, anchored to the trigger.- Record
EditDuplicateDelete- Record
EditDuplicateDelete -
Section heading (optional, when open) labels a group of related items; it is not itself selectable.- Record
EditDuplicateDelete- Record
EditDuplicateDelete -
Menu item (when open) one action. Destructive items take the danger variant and go last.- Record
EditDuplicateDelete- Record
EditDuplicateDelete -
Divider (optional, when open) separates groups of items without naming them.- Record
EditDuplicateDelete- Record
EditDuplicateDelete
When to use
Section titled “When to use”A dropdown menu holds actions, not choices. Choosing an item runs something and the menu closes; nothing in it stays selected, and there is no value to read back. When the reader is picking a value that persists, use a Select or a Combobox, which have somewhere to put it.
Use it for the secondary and contextual actions that do not need to sit in view all the time — the overflow beside a table row, the account menu behind an avatar. Navigation between views is not one of those: put it in a Tab Bar, where the reader can see where they are without opening anything.
For a surface holding a paragraph and a control or two rather than a list of actions, see Popover, which sets out the boundary between the three surfaces that open over a page.
Menu items
Section titled “Menu items”Destructive items go last
Section titled “Destructive items go last”A menu opens under the pointer, so the item nearest the trigger is the one the reader reaches by accident. Putting Delete at the bottom costs a deliberate movement to get to, and that movement is the only protection an item has before it fires.
Label the item for what it destroys rather than leaving the risk to color.
variant="danger" on DropdownMenu.Item turns the row red, and red is not a
warning to anyone who cannot see it. The props table below covers the menu
itself; the items take their own set, including variant, prefixIcon,
subtitle and disabled.
Seven items is the working limit
Section titled “Seven items is the working limit”Seven items is the working limit. A menu has no search and no scroll position,
so a long one is worked through from the top every time it opens. Split it with
DropdownMenu.Divider, or move the whole set somewhere with room.
DropdownMenu.Heading labels a group visually and does nothing else: it renders
as a list item with no ARIA role and no association to the items beneath it, so a
screen reader reads the heading as one more line of text rather than as the name
of a group. Prefer a divider where the grouping has to be understood by everyone.
Behavior
Section titled “Behavior”Position the menu against its trigger
Section titled “Position the menu against its trigger”The menu measures itself against the trigger element and takes its placement
entirely from it. side picks the edge and align the alignment along that
edge; align defaults to end, which is why an unconfigured menu hangs from the
trigger’s right.
portal decides where the menu lives in the document, which is a different
question from where it appears. It defaults to false, so the menu renders
inside the trigger’s own subtree — turn it on when an ancestor with
overflow: hidden or a stacking context clips the surface.
Give the menu at least one item
Section titled “Give the menu at least one item”With no children, DropdownMenu returns nothing at all, and that includes the
trigger. A menu built from a filtered list can therefore make its own button
disappear when the filter empties, which reads as a broken page rather than an
empty menu. Render the trigger yourself with a disabled state when there is a
chance of having nothing to offer.
disabled is the softer version: it keeps the trigger and drops the menu, so
clicking opens nothing.
The page behind a menu stays live
Section titled “The page behind a menu stays live”modal is forced off, so the page keeps scrolling and everything behind the menu
stays clickable. That is what a menu should do, because a menu is a detour and
not an interruption, and it is the line between this and
Modal.
Accessibility
Section titled “Accessibility”Nothing in a menu’s ARIA is left to set
Section titled “Nothing in a menu’s ARIA is left to set”Every association a menu needs is already in the markup. Measured on this page’s
own specimen: the trigger carries aria-haspopup="menu", aria-expanded and
aria-controls; the surface carries role="menu" and an aria-labelledby
pointing back at the trigger; each item carries role="menuitem"; each divider
carries role="separator". The keyboard behavior underneath is Radix’s menu
implementation: arrow keys, Home, End, Escape and type-ahead, with focus moving
into the menu on open and back to the trigger on close.
So setting any of these by hand either duplicates what is there or overwrites something correct. The one thing left to you is the trigger’s own accessible name, which is an ordinary Button problem when the trigger is an icon.
Content
Section titled “Content”Leave out of the item what the trigger already says
Section titled “Leave out of the item what the trigger already says”The trigger stays on screen while the menu is open, so the reader can see what the menu belongs to. “Delete” beside a row already means delete that row, and “Delete this candidate’s record” spends a line repeating something already on the screen.
That only holds while the trigger says what it is about. An overflow icon says nothing, so its items carry the subject themselves.
Capitalization, punctuation and the rules that apply to every label are on Writing.
| Name | Type |
|---|---|
id |
|
className |
|
triggerrequired |
|
contentProps |
Props passed to internal DropdownMenu.Content component. |
triggerProps |
Props passed to internal DropdownMenu.Trigger component. |
portalProps |
Props passed to internal DropdownMenu.Portal component. |
portal |
Whether or not to wrap menu with DropdownMenu.Portal component. |
disabled |
Whether or not clicks render menu items. |
zIndex |
|
Primitive.DropdownMenuProps and Pick<Primitive.DropdownMenuContentProps, ‘side’ | ‘align’>.Was this page helpful?