Dropdown Menu
A list of actions or options that opens from a button.
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button variant="outline" size="icon" aria-label="Alert actions" />
}
>
<MoreHorizontalIcon />
</DropdownMenuTrigger>
<DropdownMenuContent className="w-52">
<DropdownMenuItem>
<EyeIcon />
View details
</DropdownMenuItem>
<DropdownMenuItem>
<UserPlusIcon />
Assign to me
<DropdownMenuShortcut>A</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CopyIcon />
Copy alert ID
<DropdownMenuShortcut>⌘C</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<Trash2Icon />
Delete alert
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Usage
import { Button } from "@zenoxai/ui/components/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@zenoxai/ui/components/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Actions
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={viewDetails}>View details</DropdownMenuItem>
<DropdownMenuItem onClick={assign}>Assign to me</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive" onClick={remove}>
Delete alert
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Use a dropdown menu to group actions behind one button: the actions of a table row, the settings of a tenant, export options. Items run on click and the menu closes.
| Need | Use |
|---|---|
| The same actions on right-click, as a shortcut | Context Menu |
| A row of menus at the top of an editor or app (“Report”, “Edit”, “View”) | Menubar |
| A value for a form field | Select or Combobox |
| A searchable list of many commands | Command |
| Controls other than items (inputs, sliders) | Popover |
DropdownMenuContent opens below the trigger, aligned to its start edge, and is as wide as the
trigger with a minimum of 9rem. Set a width with className (w-52) when the trigger is
narrow, as with an icon button. Put the destructive item last, after a separator.
Examples
Groups and labels
Separate related items with DropdownMenuSeparator and head each group with a
DropdownMenuLabel. A disabled item stays visible, so the user learns the action exists.
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Acme Corp
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>Tenant</DropdownMenuLabel>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Users and roles</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuLabel>Data</DropdownMenuLabel>
<DropdownMenuItem>Integrations</DropdownMenuItem>
<DropdownMenuItem disabled>Data retention</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>Checkbox items
DropdownMenuCheckboxItem toggles a setting and keeps the menu open, so the user can change
several in a row. Control it with checked and onCheckedChange.
function DropdownMenuCheckboxes() {
const [columns, setColumns] = React.useState({
severity: true,
asset: true,
tenant: false,
assignee: true,
})
return (
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Columns
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuLabel>Show columns</DropdownMenuLabel>
{(
[
["severity", "Severity"],
["asset", "Asset"],
["tenant", "Tenant"],
["assignee", "Assignee"],
] as const
).map(([key, label]) => (
<DropdownMenuCheckboxItem
key={key}
checked={columns[key]}
onCheckedChange={(checked) =>
setColumns((current) => ({ ...current, [key]: checked }))
}
>
{label}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}Radio items
DropdownMenuRadioGroup holds a single choice, such as the sort order of a list. Control it with
value and onValueChange.
function DropdownMenuRadio() {
const [sort, setSort] = React.useState("severity")
return (
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Sort alerts
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuLabel>Sort by</DropdownMenuLabel>
<DropdownMenuRadioGroup value={sort} onValueChange={setSort}>
<DropdownMenuRadioItem value="severity">
Severity
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="newest">
Newest first
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="oldest">
Oldest first
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
)
}Submenu
DropdownMenuSub nests a menu that opens to the side on hover, click or the right arrow key.
Keep to one level.
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Export
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuItem>Current page</DropdownMenuItem>
<DropdownMenuItem>All 1,284 alerts</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>Format</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>CSV</DropdownMenuItem>
<DropdownMenuItem>JSON</DropdownMenuItem>
<DropdownMenuItem>PDF report</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>Accessibility
- The trigger has
aria-haspopupandaria-expanded. Enter, Space and the arrow keys open the menu; arrow keys move between items, typing a letter jumps to the matching item, and Escape closes the menu and returns focus to the trigger. - An icon-only trigger needs an
aria-labelthat names the menu (“Alert actions”). DropdownMenuShortcutonly displays the shortcut. Bind the key yourself.DropdownMenuLabelis a visual heading and is not announced as the group’s name.
API reference
Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.
DropdownMenu
DropdownMenu is a list of actions or options opened from a button: the actions of a row, a user menu, a "More" overflow. To pick a form value, use Select; for a searchable list of commands, Command.
| Prop | Type | Default |
|---|---|---|
actionsRef | RefObject<MenuRootActions | null>A ref to imperative actions. - `unmount`: Ends the closing phase of the menu after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the menu completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the menu imperatively when called. | - |
closeParentOnEsc | booleanWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu. | false |
defaultOpen | booleanWhether the menu is initially open. | false |
defaultTriggerId | string | nullID of the trigger that the menu is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open menu. | - |
disabled | booleanWhether the component should ignore user interaction. | false |
handle | MenuHandle<unknown>A handle to associate the menu with a trigger. If specified, allows external triggers to control the menu's open state. | - |
highlightItemOnHover | booleanWhether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. | true |
loopFocus | booleanWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | true |
modal | booleanDetermines if the menu enters a modal state when open. - `true`: user interaction is limited to the menu: document page scroll is locked and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. | true |
onItemHighlighted | ((highlightedItem: HTMLElement, eventDetails: MenuRootHighlightEventDetails) => void)Callback fired when an item is highlighted or unhighlighted. Receives the highlighted item element (or `undefined` if no item is highlighted) and details containing the reason for the change, the event, and the item's text label. The `reason` can be: - `'keyboard'`: the highlight changed due to keyboard navigation. - `'pointer'`: the highlight changed due to pointer hovering. The event may be a `MouseEvent` rather than a `PointerEvent`. - `'none'`: the highlight changed for another reason, such as automatic highlighting while filtering, the item list changing, or the popup opening or closing. | - |
onOpenChange | ((open: boolean, eventDetails: MenuRootChangeEventDetails) => void)Event handler called when the menu is opened or closed. | - |
onOpenChangeComplete | ((open: boolean) => void)Event handler called after any animations complete when the menu is opened or closed. | - |
open | booleanWhether the menu is currently open. | - |
orientation | "horizontal" | "vertical"The visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. | 'vertical' |
triggerId | string | nullID of the trigger that the menu is associated with. This is useful in conjunction with the `open` prop to create a controlled menu. There's no need to specify this prop when the menu is uncontrolled (that is, when the `open` prop is not set). | - |
DropdownMenuPortal
DropdownMenuPortal moves the menu to the end of the document body. DropdownMenuContent renders it already.
| Prop | Type | Default |
|---|---|---|
container | HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | nullA parent element to render the portal element into. | - |
keepMounted | booleanWhether to keep the portal mounted in the DOM while the popup is hidden. | false |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuPortalState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
DropdownMenuTrigger
| Prop | Type | Default |
|---|---|---|
closeDelay | numberHow long to wait before closing the menu that was opened on hover. Specified in milliseconds. | 0 |
delay | numberHow long to wait before the menu may be opened on hover. Specified in milliseconds. | 100 |
disabled | booleanWhether the component should ignore user interaction. | false |
handle | MenuHandle<unknown>A handle to associate the trigger with a menu. | - |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
openOnHover | booleanWhether the menu should also open when the trigger is hovered. | - |
payload | unknownA payload to pass to the menu when it is opened. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuTriggerState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
DropdownMenuContent
DropdownMenuContent is the menu popup, in a portal below the trigger, as wide as the trigger and at least 144px.
| Prop | Type | Default |
|---|---|---|
align | "center" | "start" | "end"How to align the popup relative to the specified side. | start |
alignOffset | number | OffsetFunctionAdditional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment. | 0 |
finalFocus | boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)Determines the element to focus when the menu is closed. | - |
id | string@ignore | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuPopupState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
side | "left" | "right" | "top" | "bottom" | "inline-end" | "inline-start"Which side of the anchor element to align the popup against. May automatically change to avoid collisions. | bottom |
sideOffset | number | OffsetFunctionDistance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. | 0 |
DropdownMenuGroup
| Prop | Type | Default |
|---|---|---|
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuGroupState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
DropdownMenuLabel
DropdownMenuLabel is a section heading inside the menu. It is a plain div, not linked to the items under it, because Base UI's GroupLabel throws outside a Group. When the items need that aria-labelledby association, wrap them in a DropdownMenuGroup with `Menu.GroupLabel` from "@base-ui/react/menu".
| Prop | Type | Default |
|---|---|---|
inset | booleanIndents the text to line up with items that have a leading icon. | - |
DropdownMenuItem
| Prop | Type | Default |
|---|---|---|
closeOnClick | booleanWhether to close the menu when the item is clicked. | true |
disabled | booleanWhether the component should ignore user interaction. | false |
id | string@ignore | - |
inset | booleanIndents the text to line up with items that have a leading icon. | - |
label | stringOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | - |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
onClick | ((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)The click handler for the menu item. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuItemState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
variant | "default" | "destructive"`destructive` colors the item red, for delete and other risky actions. | default |
DropdownMenuCheckboxItem
| Prop | Type | Default |
|---|---|---|
checked | booleanWhether the checkbox item is currently ticked. | - |
closeOnClick | booleanWhether to close the menu when the item is clicked. | false |
defaultChecked | booleanWhether the checkbox item is initially ticked. | false |
disabled | booleanWhether the component should ignore user interaction. | false |
id | string@ignore | - |
inset | booleanIndents the text to line up with items that have a leading icon. | - |
label | stringOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | - |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
onCheckedChange | ((checked: boolean, eventDetails: MenuRootChangeEventDetails) => void)Event handler called when the checkbox item is ticked or unticked. | - |
onClick | ((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)The click handler for the menu item. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuCheckboxItemState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
DropdownMenuRadioGroup
| Prop | Type | Default |
|---|---|---|
defaultValue | anyThe uncontrolled value of the radio item that should be initially selected. | - |
disabled | booleanWhether the component should ignore user interaction. | false |
onValueChange | ((value: any, eventDetails: MenuRootChangeEventDetails) => void)Function called when the selected value changes. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuRadioGroupState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
value | anyThe controlled value of the radio item that should be currently selected. | - |
DropdownMenuRadioItem
| Prop | Type | Default |
|---|---|---|
closeOnClick | booleanWhether to close the menu when the item is clicked. | false |
disabled | booleanWhether the component should ignore user interaction. | false |
id | string@ignore | - |
inset | booleanIndents the text to line up with items that have a leading icon. | - |
label | stringOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | - |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
onClick | ((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void)The click handler for the menu item. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuRadioItemState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
value* | anyValue of the radio item. This is the value that will be set in the MenuRadioGroup when the item is selected. | - |
DropdownMenuSeparator
| Prop | Type | Default |
|---|---|---|
orientation | "horizontal" | "vertical"The orientation of the separator. | vertical |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SeparatorState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
DropdownMenuShortcut
DropdownMenuShortcut is the key hint at the right edge of an item. It is display text only: bind the keys yourself.
No props of its own. Accepts the props of the element it renders.
DropdownMenuSub
| Prop | Type | Default |
|---|---|---|
actionsRef | RefObject<MenuRootActions | null>A ref to imperative actions. - `unmount`: Ends the closing phase of the menu after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the menu completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the menu imperatively when called. | - |
closeParentOnEsc | booleanWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu. | false |
defaultOpen | booleanWhether the menu is initially open. | false |
disabled | booleanWhether the component should ignore user interaction. | false |
highlightItemOnHover | booleanWhether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state. | true |
loopFocus | booleanWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | true |
onItemHighlighted | ((highlightedItem: HTMLElement, eventDetails: MenuRootHighlightEventDetails) => void)Callback fired when an item is highlighted or unhighlighted. Receives the highlighted item element (or `undefined` if no item is highlighted) and details containing the reason for the change, the event, and the item's text label. The `reason` can be: - `'keyboard'`: the highlight changed due to keyboard navigation. - `'pointer'`: the highlight changed due to pointer hovering. The event may be a `MouseEvent` rather than a `PointerEvent`. - `'none'`: the highlight changed for another reason, such as automatic highlighting while filtering, the item list changing, or the popup opening or closing. | - |
onOpenChange | ((open: boolean, eventDetails: MenuRootChangeEventDetails) => void)Event handler called when the menu is opened or closed. | - |
onOpenChangeComplete | ((open: boolean) => void)Event handler called after any animations complete when the menu is opened or closed. | - |
open | booleanWhether the menu is currently open. | - |
orientation | "horizontal" | "vertical"The visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys. | 'vertical' |
DropdownMenuSubTrigger
| Prop | Type | Default |
|---|---|---|
closeDelay | numberHow long to wait before closing the menu that was opened on hover. Specified in milliseconds. | 0 |
delay | numberHow long to wait before the menu may be opened on hover. Specified in milliseconds. | 100 |
disabled | booleanWhether the component should ignore user interaction. | false |
id | string@ignore | - |
inset | booleanIndents the text to line up with items that have a leading icon. | - |
label | stringOverrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. | - |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
onClick | ((event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void) | - |
openOnHover | booleanWhether the menu should also open when the trigger is hovered. | true |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuSubmenuTriggerState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
DropdownMenuSubContent
| Prop | Type | Default |
|---|---|---|
align | "center" | "start" | "end"How to align the popup relative to the specified side. | start |
alignOffset | number | OffsetFunctionAdditional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment. | 0 |
finalFocus | boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)Determines the element to focus when the menu is closed. | - |
id | string@ignore | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuPopupState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
side | "left" | "right" | "top" | "bottom" | "inline-end" | "inline-start"Which side of the anchor element to align the popup against. May automatically change to avoid collisions. | bottom |
sideOffset | number | OffsetFunctionDistance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. | 0 |