Dropdown combines a positioned popup, visual rows, keyboard navigation, focus, and dismissal. Your application owns data, filtering, fetching, pagination, and selected values. Import it from twenty-ui/components.
Dropdown.Content accepts numeric offsets or functions for sideOffset and alignOffset. Offset functions receive the anchor and popup dimensions, side, and alignment. Use them to preserve an anchored field picker’s overlap when the cell is scaled.
Choose an interaction type
Settype on Dropdown.Root to match the popup’s contents.
Pickers allow creation actions and selectable values in the same list, in any order. Options expose their selected state with
aria-pressed; commands keep their button semantics. Omit selected on an option that navigates or applies without a selection state, so it exposes no pressed state. Give Search an accessible name.
Content takes its accessible name from Trigger, or from SubmenuTrigger in a submenu. Pass aria-label on Content when the trigger shows a value, such as the selected option, or when the root has no trigger. An explicit aria-label or aria-labelledby, and a Title inside a Header, take precedence over the trigger name.
The underlying Menu, Select, and Popover remain available for direct composition.
Anatomy
Content includes the portal and positioner. Use width, side, align, sideOffset, and alignOffset to control placement, and collisionPadding to keep the popup further from the edges of the viewport before it shifts or flips (5 pixels by default). Section groups rows with shared spacing, and Separator divides sections. A scrollable Section caps its height and scrolls; nest labelled sections inside it to scroll them as one list. Rows use ListItem for their appearance.
render so the trigger remains one interactive element. Custom components must forward the supplied props and ref. ActionItem also accepts render for links.
Headers
Header lays out a row at the top of the popup. Put a Title in it to name the popup after the title instead of the trigger. Close closes the popup the same way an outside press does. It renders an icon button with an X by default, so give it an aria-label, or pass render and children for another control. Other controls, such as a button that returns to a previous step, can sit in the same row. Use Dropdown.Back when navigating the root’s page history.
Search and selection
Search provides the input and navigation into the result rows. Pass value and onValueChange; compute or fetch matching results in your application. Render Loading while a request is pending and Empty when no results match. Both accept caller-provided status text.
Search holds text, the first enabled OptionItem is highlighted and Enter activates it. Action rows are never picked this way. With an empty search, Enter does nothing; ArrowDown moves focus into the results. When rows are pinned above the matches, such as the current value or a “none” row, render them only when they match the search text, so Enter picks the first real match. The popup stays open when no option matches. Filtering remains caller-owned.
Actions and single selection close by default. Set multiple on the root to keep option selection open. The application still owns the selected values. closeOnClick on actions and closeOnSelect on options override those defaults, including repeated actions or asynchronous progress. Use disabled to prevent activation while retaining the row’s appearance.
Icon grids
Setcolumns on Section to lay out items in equal-width grid columns. The same count drives keyboard navigation: Left and Right move within a row, while Up and Down move between rows. Navigation skips disabled options and follows the reading direction. When no enabled option is left in the column, Up and Down move to the nearest enabled option of the next row. Up from the first row returns to the search field when no earlier section exists. A short final row uses its last item when the requested column is missing. Inside a submenu, the backward arrow closes the submenu when no enabled option remains to its left.
Render each icon as an OptionItem with an accessible label and an icon button through render. Use indicator="none" to show selection through the button background.
Pages and submenus
UsePage for navigation that replaces the contents of the same popup. Wrap the first page in Page id="root", or choose another initial page with defaultPage. Set page on an action to navigate without closing. Back returns to the previous page and restores focus to its invoking row. Closing the dropdown resets its page history.
A page can override the root’s type. For example, an action menu can open a searchable picker page or a form panel. Search receives focus when entering a searchable page; other pages focus their first row after Back.
useDropdownPage in a component rendered inside the root. It returns the current page, goToPage(page), goBack() and canGoBack. For example, an option can return to the previous page after a selection while the popup stays open:
Submenu with SubmenuTrigger and a nested Content for a side-by-side menu. Arrow Right enters the submenu; Arrow Left returns to its parent. Selecting a closing action closes the whole dropdown.
Independent nested pickers
Place anotherDropdown.Root inside Content when a control opens its own picker, such as a sort direction selector. Selecting an inner option closes only that picker and restores focus to its trigger. Escape and outside presses dismiss one layer at a time. Use Submenu for commands that should close the entire menu tree when selected.
Panels and controlled state
Usetype="panel" for form inputs. Input interaction keeps the panel open; a save action can close it. Own open and onOpenChange when another component or a shortcut needs to open the dropdown. Keep that state with the feature that coordinates the interaction.
Escape dismisses the popup and restores focus to its trigger. Outside interaction also dismisses it, and the click that dismisses it doesn’t activate what it lands on. Keys pressed inside the popup stay inside it, except Ctrl or Cmd shortcuts the dropdown doesn’t handle, so page-level shortcuts keep working. Content accepts focus and portal options from Popover, including an explicit return-focus target for editor workflows.
Dismissal
Escape, a press outside the popup and Tab out of it close the dropdown. PassonEscapeKeyDown or onInteractOutside to Root to react before it closes, and call event.preventDefault() to keep it open. event.target is the element pressed outside, or null when focus leaves with Tab. A prevented outside press still reaches the element it lands on.
Anchoring without a trigger
A root can open without aTrigger, for example as a context menu. Control it with open and onOpenChange, and pass anchor to Content: an element, a ref, or a virtual element that returns a rectangle in viewport coordinates. Give Content an aria-label, since there is no trigger to name it after. On close, focus returns to the element that was focused when the popup opened.
Labels
Each part owns its text: supplySearch.placeholder and native accessible names, Close accessible attributes, and children for Back, Loading, and Empty. Back defaults to Back; loading and empty states have no built-in message. Item parts accept shortcutJoinLabel to replace the default then between sequential shortcut steps. Native aria-labelledby takes precedence over aria-label.
Props
Dropdown.Root
boolean
Whether the popup is initially open when uncontrolled.
string
ID of the
Page shown when the popup opens. Closing the popup resets the page history to it. Defaults to root.boolean
Allows selecting several options. Options then keep the popup open and show a checkbox by default.
((event: DropdownDismissEvent) => void)
Called when Escape is about to close the popup. Call
event.preventDefault() to keep it open.((event: DropdownDismissEvent) => void)
Called when a press outside the popup, or Tab out of it, is about to close it.
event.target is the element pressed outside, or null after Tab. Call event.preventDefault() to keep it open.((open: boolean) => void)
Called with the next open state when the popup opens or closes.
boolean
Whether the popup is open. Use with
onOpenChange to control it."menu" | "panel" | "picker"
required
Interaction model:
menu for commands and links, picker for search and selectable options, or panel for forms. It sets the ARIA roles, initial focus, and keyboard navigation.Dropdown.Trigger
string | ((state: PopoverTriggerState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
number
default:"0"
How long to wait before closing the popover that was opened on hover.
Specified in milliseconds.Requires the
openOnHover prop.number
default:"300"
How long to wait before the popover may be opened on hover. Specified in milliseconds.Requires the
openOnHover prop.string
ID of the trigger. In addition to being forwarded to the rendered element,
it is also used to specify the active trigger for the popover in controlled mode (with the PopoverRoot
triggerId prop).boolean
default:"true"
Whether 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 (e.g. <div>).boolean
default:"false"
Whether the popover should also open when the trigger is hovered.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTriggerState>
Allows you to replace the component’s HTML element
with a different tag, or compose it with another component.Accepts a
ReactElement or a function that returns the element to render.CSSProperties | ((state: PopoverTriggerState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
Dropdown.Content
"center" | "end" | "start"
default:"start"
Alignment of the popup along the anchor.
number | OffsetFunction
Offset in pixels along the alignment axis.
Element | VirtualElement | RefObject<Element | null> | (() => Element | VirtualElement | null) | null
Element or position the popup is anchored to. Defaults to the trigger.
string | ((state: PopoverPopupState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
Padding
Space in pixels kept between the popup and the edges of its collision
boundary, for all sides or per side. Defaults to 5.
HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null
Element the popup is portaled into. Defaults to the theme’s portal
container.
boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)
Determines the element to focus when the popover is closed.
false: Do not move focus.true: Move focus based on the default behavior (trigger or previously focused element).RefObject: Move focus to the ref element.function: Called with the interaction type (mouse,touch,pen, orkeyboard). Return an element to focus,trueto use the default behavior,nullto fall back to the default behavior, orfalse/undefinedto do nothing.
boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)
Determines the element to focus when the popover is opened.
By default, focus moves to the first tabbable element inside the popup, except when the popover
is opened by touch — then the popup itself is focused to avoid opening the virtual keyboard.
false: Do not move focus.true: Move focus based on the default behavior (first tabbable element or popup).RefObject: Move focus to the ref element.function: Called with the interaction type (mouse,touch,pen, orkeyboard). Return an element to focus,trueto use the default behavior,nullto fall back to the default behavior, orfalse/undefinedto do nothing.
boolean
Keeps the popup mounted in the DOM while the popover is closed.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverPopupState>
Allows you to replace the component’s HTML element
with a different tag, or compose it with another component.Accepts a
ReactElement or a function that returns the element to render."bottom" | "inline-end" | "inline-start" | "left" | "right" | "top"
Side of the anchor the popup is placed on.
number | OffsetFunction
default:"0"
Distance in pixels between the anchor and the popup.
CSSProperties | ((state: PopoverPopupState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
Width<string | number>
default:"200"
CSS width of the popup. Numbers are in pixels.
Dropdown.ActionItem
string
CSS class applied to the row.
boolean
default:"true"
Closes the dropdown after activation, along with parent menus when inside a submenu. Ignored when
page is set."danger" | "neutral"
Color of the text and icons.
danger marks a destructive action.ReactNode
Supporting text, placed according to
descriptionPlacement."end" | "inline"
Where the description renders: inline after the content or at the end of
the row.
ReactNode
Icon rendered after the content.
boolean
default:"false"
Whether the button should be focusable when disabled.
Shows a chevron indicating that the item opens a submenu.
boolean
default:"true when render is omitted; false otherwise"
Whether 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>).string
ID of the
Page to show on activation. The popup stays open, and the row shows a chevron by default.ReactElement<unknown, string | JSXElementConstructor<any>>
Element rendered as the row instead of a native button. Custom components must forward the supplied props and ref.
ShortcutDefinition
Keyboard shortcut keys displayed at the end of the row. Registering the
shortcut is up to the application.
string
Text between sequential shortcut steps. Defaults to
then.ReactNode
Icon rendered before the content.
CSSProperties
Inline styles applied to the row.
Dropdown.OptionItem
string
CSS class applied to the row.
boolean
Closes the dropdown after selection. Defaults to
true, or false when multiple is set."danger" | "neutral"
Color of the text and icons.
danger marks a destructive action.ReactNode
Supporting text, placed according to
descriptionPlacement."end" | "inline"
Where the description renders: inline after the content or at the end of
the row.
ReactNode
Icon rendered after the content.
boolean
default:"false"
Whether the button should be focusable when disabled.
Shows a chevron indicating that the item opens a submenu.
"check" | "checkbox" | "none"
Selection indicator: a check icon after the content, a checkbox before it, or none. Defaults to
checkbox when multiple is set, otherwise check, and to none when selected is omitted.boolean
default:"true when render is omitted; false otherwise"
Whether 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>).(() => void)
Called when the option is activated. The application owns the selected value.
ReactElement<unknown, string | JSXElementConstructor<any>>
Element rendered as the row instead of a native button. Custom components must forward the supplied props and ref.
boolean
Whether the option is selected. Exposed as
aria-checked in menus and aria-pressed in other dropdown types, or as aria-current when render is not a button, such as a link. Omit it for options that navigate or apply without a selection state.ShortcutDefinition
Keyboard shortcut keys displayed at the end of the row. Registering the
shortcut is up to the application.
string
Text between sequential shortcut steps. Defaults to
then.ReactNode
Icon rendered before the content.
CSSProperties
Inline styles applied to the row.
Dropdown.Search
string | ((state: InputState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
string | number | readonly string[]
The default value of the input. Use when uncontrolled.
((value: string) => void)
Called with the search text on every change. The application filters the results.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, InputState>
Allows you to replace the component’s HTML element
with a different tag, or compose it with another component.Accepts a
ReactElement or a function that returns the element to render.CSSProperties | ((state: InputState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
string | number | readonly string[]
The value of the input. Use when controlled.
Dropdown.Header
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>
Allows you to replace the component’s HTML element
with a different tag, or compose it with another component.Accepts a
ReactElement or a function that returns the element to render.Dropdown.Title
string | ((state: PopoverTitleState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTitleState>
Allows you to replace the component’s HTML element
with a different tag, or compose it with another component.Accepts a
ReactElement or a function that returns the element to render.CSSProperties | ((state: PopoverTitleState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
Dropdown.Close
string
required
Accessible name of the close control. Required because the default control only shows an icon.
string | ((state: PopoverCloseState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
boolean
default:"true"
Whether 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>).ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverCloseState>
Allows you to replace the component’s HTML element
with a different tag, or compose it with another component.Accepts a
ReactElement or a function that returns the element to render.CSSProperties | ((state: PopoverCloseState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
Dropdown.Page
string
required
Identifier matched by
defaultPage, the page prop of actions, and goToPage. The page renders only while it is current."menu" | "panel" | "picker"
Interaction model while the page is shown. Defaults to the root
type.Dropdown.Back
string
CSS class applied to the row.
"danger" | "neutral"
Color of the text and icons.
danger marks a destructive action.ReactNode
Supporting text, placed according to
descriptionPlacement."end" | "inline"
Where the description renders: inline after the content or at the end of
the row.
ReactNode
Icon rendered after the content.
boolean
default:"false"
Whether the button should be focusable when disabled.
Shows a chevron indicating that the item opens a submenu.
boolean
default:"true when render is omitted; false otherwise"
Whether 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>).ReactElement<unknown, string | JSXElementConstructor<any>>
Element rendered as the row instead of a native button. Custom components must forward the supplied props and ref.
ShortcutDefinition
Keyboard shortcut keys displayed at the end of the row. Registering the
shortcut is up to the application.
string
Text between sequential shortcut steps. Defaults to
then.ReactNode
Icon rendered before the content.
CSSProperties
Inline styles applied to the row.
Dropdown.Submenu
Whether the popup is initially open when uncontrolled.
ID of the
Page shown when the popup opens. Closing the popup resets the page history to it. Defaults to root.Allows selecting several options. Options then keep the popup open and show a checkbox by default.
Called with the next open state when the popup opens or closes.
Whether the popup is open. Use with
onOpenChange to control it.Interaction model:
menu for commands and links, picker for search and selectable options, or panel for forms. It sets the ARIA roles, initial focus, and keyboard navigation.Dropdown.SubmenuTrigger
CSS class applied to the row.
Delay in milliseconds before a submenu opened on hover closes. Requires
openOnHover. Defaults to 0.Color of the text and icons.
danger marks a destructive action.Delay in milliseconds before the submenu opens on hover. Requires
openOnHover. Defaults to 300.Supporting text, placed according to
descriptionPlacement.Where the description renders: inline after the content or at the end of
the row.
Icon rendered after the content.
Whether the button should be focusable when disabled.
Shows a chevron indicating that the item opens a submenu.
Whether 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>).Also opens the submenu when the row is hovered.
Element rendered as the row instead of a native button. Custom components must forward the supplied props and ref.
Keyboard shortcut keys displayed at the end of the row. Registering the
shortcut is up to the application.
Text between sequential shortcut steps. Defaults to
then.Icon rendered before the content.
Inline styles applied to the row.
Dropdown.Section
number
Number of grid columns. Sets the grid layout and enables horizontal arrow navigation and vertical movement by row.
ReactNode
Heading displayed above the rows. It also names the group for assistive technologies.
boolean
Caps the section height and scrolls its rows. Nest labelled sections inside it to scroll them as one list.
Dropdown.Separator
string
Class applied to the separator. Native div attributes and refs are also accepted.
Dropdown.Loading
ReactNode
Loading message announced as a polite, busy status.
string
Class applied to the status. Native div attributes and refs are also accepted.
Dropdown.Empty
ReactNode
Empty-state message announced as a polite status.
string
Class applied to the status. Native div attributes and refs are also accepted.