A component that lets users choose one option from a list.
Prefer SelectControl when using with a standard label and description.
When using object values, pass an items array so Select.Trigger can
auto-resolve the selected item’s label. By default, items should use a
{ value, label } shape, or provide itemToStringLabel for a custom shape.
Object values are compared with Object.is by default, so use the same
object references for value / defaultValue and Select.Item values, or
provide isItemEqualToValue.
import { Select } from '@wordpress/ui';
LinksPermalink to this section
PropsPermalink to this section
| Name | Default | Description |
|---|---|---|
name |
Identifies the field when a form is submitted. | |
form |
Identifies the form that owns the hidden input. Useful when the select is rendered outside the form. | |
children |
| |
id |
The id of the Select. | |
readOnly | false |
Whether the user should be unable to choose a different option from the select popup. |
defaultValue |
The uncontrolled value of the select when it’s initially rendered. To render a controlled select, use the | |
required | false |
Whether the user must choose a value before submitting a form. |
disabled | false |
Whether the component should ignore user interaction. |
value |
The value of the select. Use when controlled. | |
onValueChange |
Event handler called when the value of the select changes. | |
open |
Whether the select popup is currently open. | |
defaultOpen | false |
Whether the select popup is initially open. To render a controlled select popup, use the |
onOpenChange |
Event handler called when the select popup is opened or closed. | |
autoComplete |
Provides a hint to the browser for autofill. | |
itemToStringValue |
When the item values are objects ( | |
isItemEqualToValue |
Custom comparison logic used to determine if a select item value matches the current selected value. Useful when item values are objects without matching referentially.
Defaults to | |
itemToStringLabel |
When the item values are objects ( | |
highlightItemOnHover | true |
Whether moving the pointer over items should highlight them.
Disabling this prop allows CSS |
actionsRef |
A ref to imperative actions.
| |
items |
Data structure of the items rendered in the select popup.
When specified, | |
onOpenChangeComplete |
Event handler called after any animations complete when the select popup is opened or closed. | |
inputRef |
A ref to access the hidden input element. | |
modal | true |
Determines if the select enters a modal state when open.
On touch devices, a |
ExamplesPermalink to this section
DefaultPermalink to this section
const Default = () => <Select.Root items={defaultItems}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ defaultItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
CompactPermalink to this section
const Compact = () => <Select.Root>{[
<Select.Trigger size="compact" aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ defaultItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
MinimalPermalink to this section
The minimal variant must be used judiciously, because in many
contexts it can be unclear to users that it is a select trigger.
Combined with the small size, minimal can be used to create a
very low-profile Select, intended for rare use cases like
a pagination control.
const Minimal = () => <Select.Root defaultValue="1">{[
<Select.Trigger
size="small"
variant="minimal"
aria-label="Item"
key="trigger"
/>,
<Select.Popup width="content" key="popup">
{ Array.from( { length: 6 }, ( _, index ) => (
<Select.Item
key={ index }
value={ `${ index + 1 }` }
size="small"
>
<Select.ItemLabel>{ `${
index + 1
}` }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
GroupedPermalink to this section
Options can be organized into labeled groups with Select.Group
and Select.GroupLabel.
const Grouped = () => <Select.Root items={groupedItems.flatMap( ( group ) => group.items )}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ groupedItems.map( ( group ) => (
<Select.Group key={ group.label }>
<Select.GroupLabel>{ group.label }</Select.GroupLabel>
{ group.items.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>
{ item.label }
</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Group>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Custom PlaceholderPermalink to this section
Use the placeholder prop on Select.Trigger to show text when no
value is selected. The default placeholder is "Select".
const WithCustomPlaceholder = () => <Select.Root items={defaultItems}>{[
<Select.Trigger
placeholder="Choose an item"
aria-label="Item"
key="trigger"
/>,
<Select.Popup key="popup">
{ defaultItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Null Value OptionPermalink to this section
Use a null item when users should be able to clear the selected value from
the popup. When items includes a null item, its label is used as the
placeholder text.
const WithNullValueOption = () => <Select.Root items={nullValueOptionItems}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ nullValueOptionItems.map( ( item ) => (
<Select.Item
key={ item.value ?? 'null' }
value={ item.value }
>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
LabelingPermalink to this section
When accessibly labeling a Select, note that the label must be associated with the Select.Trigger,
not the Select.Root.
Whether labeling with aria-label, htmlFor, or aria-labelledby, the association must be made to the Select.Trigger.
const Labeling = () => <Select.Root>{[
<Select.Trigger aria-label="User role" key="trigger" />,
<Select.Popup key="popup">
{ defaultItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With OverflowPermalink to this section
const WithOverflow = () => <Select.Root items={overflowItems} defaultValue={overflowItems[ 0 ]}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ overflowItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Long ListPermalink to this section
const WithLongList = () => <Select.Root items={longListItems} defaultValue={longListItems[ 17 ]}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ longListItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
DisabledPermalink to this section
const Disabled = () => <Select.Root defaultValue={defaultItems[ 0 ]} disabled>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ defaultItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Disabled ItemPermalink to this section
const WithDisabledItem = () => <Select.Root items={disabledItemItems} defaultValue={disabledItemItems[ 0 ]}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ disabledItemItems.map( ( item ) => (
<Select.Item
key={ item.value }
value={ item }
disabled={ item.disabled }
>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Custom Trigger And ItemPermalink to this section
For custom needs, a Select.Trigger can take a custom render function as its
children. Put custom item content inside Select.ItemLabel.
const WithCustomTriggerAndItem = () => <Select.Root items={customOptions} defaultValue={customOptions[ 0 ]}>{[
<Select.Trigger aria-label="Item" key="trigger">
{ ( item ) => (
<span
style={ {
display: 'flex',
alignItems: 'center',
gap: 8,
} }
>
<img
src={ `https://gravatar.com/avatar/?d=initials&name=${ item.value }` }
alt=""
width="20"
style={ {
borderRadius: '50%',
} }
/>
{ item.label }
</span>
) }
</Select.Trigger>,
<Select.Popup key="popup">
{ customOptions.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Item DescriptionPermalink to this section
Use Select.ItemDescription for supplementary text that should be
announced as a description rather than part of the item name.
const WithItemDescription = () => <Select.Root items={stockItems}>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup key="popup">
{ stockItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
<Select.ItemDescription>
{ item.description }
</Select.ItemDescription>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;
With Custom z-indexPermalink to this section
Popovers in Gutenberg are managed with explicit z-index values, which can create situations where a select popup renders below another popover when you want it above.
The --wp-ui-select-z-index CSS variable controls the z-index of the
Select positioner. Override it either:
- Globally, by setting the variable on
:rootorbody(raises everySelectpopover in the page), or - Per instance, by passing a
Select.Portalwith astyle(orclassName) toSelect.Popup‘sportalprop. The variable cascades from the portal wrapper to everything rendered inside it.
This story demonstrates the per-instance approach.
const WithCustomZIndex = () => <Select.Root>{[
<Select.Trigger aria-label="Item" key="trigger" />,
<Select.Popup
portal={
<Select.Portal
style={ { '--wp-ui-select-z-index': '9999' } }
/>
}
key="popup"
>
{ defaultItems.map( ( item ) => (
<Select.Item key={ item.value } value={ item }>
<Select.ItemLabel>{ item.label }</Select.ItemLabel>
</Select.Item>
) ) }
</Select.Popup>,
]}</Select.Root>;