A dropdown for selecting from predefined options with search support.
import { Select } from '@backstage/ui';
<Select
name="font"
label="Font Family"
options={[
{ id: 'sans', label: 'Sans-serif' },
{ id: 'serif', label: 'Serif' },
{ id: 'mono', label: 'Monospace' },
{ id: 'cursive', label: 'Cursive' },
]}
/>Select accepts one collection source at a time:
options array for standard text rows with optional descriptions,
icons, or sections.items with a child render function for domain objects or custom rows.useAsyncList result to options or items for asynchronous data
and incremental loading.The prop types prevent mixing collection sources and incompatible search or loading configurations.
Every option or item passed through options or items needs a stable id.
Dynamic render functions receive the full source item, and React Aria supplies
its identity to the rendered item component. Statically composed items may
provide their own id.
Use SelectItemText and SelectItemProfile for standard rich rows. Use the
low-level SelectItem for fully custom content, and provide its textValue
for search, accessibility, and the selected text shown in the trigger. The
presets derive selected text from their title or name.
Option sections are supported only in static options arrays.
Search is opt-in. Pass search as a shorthand for the default client-side
contains filter, or pass an object to configure it. Client search.filter
receives the full option or item and the current query. Custom filters require
an options or items source. Static composition supports the default
text-value filter. To control the query, pass search.inputValue together with
search.onInputChange. Use search.defaultInputValue to set an initial
uncontrolled query.
For server search, either pass a useAsyncList result with
search={{ mode: 'server' }}, or manage the query yourself with controlled
search.inputValue and search.onInputChange. Async sources provide loading
state and incremental loading automatically, so they cannot be combined with
manual loading or controlled server query props. Use loading only when data
fetching is managed outside the component.
| Prop | Type | Default | Description |
|---|---|---|---|
options | (Option | OptionSection)[]AsyncListSource<IdentifiedOption> | - | Options to display in the dropdown. Pass Option objects directly, or OptionSection objects to render grouped options under section headings. Pass a useAsyncList result directly for flat async options. |
items | Iterable<T>AsyncListSource<T> | - | Domain objects to render with a child function. Every item must have an id. |
children | ReactElementReactElement[](item: T) => ReactElement | - | Static item components, or a render function used together with items. |
dependencies | ReadonlyArray<unknown> | - | Values outside each item that invalidate cached dynamic item rendering. |
search | trueSelectSearch<T>SelectAsyncSearch<T> | - | Enables the search field and configures client or server search behavior. Client filters receive the full item. |
loading | { state: LoadingState; onLoadMore?: () => void } | - | Manual loading state for non-async collections. Async sources provide this automatically. |
selectionMode | singlemultiple | single | Single or multiple selection mode. |
value | KeyKey[]null | - | Controlled selected keys. Use one key for single selection or an array for multiple selection. |
defaultValue | KeyKey[] | - | Initial selected keys for uncontrolled usage. Use one key for single selection or an array for multiple selection. |
onChange | (key: Key | null) => void(keys: Key[]) => void | - | Called when selection changes. |
selectedKeydeprecated | Key | - | Deprecated compatibility alias for value. |
defaultSelectedKeydeprecated | Key | - | Deprecated compatibility alias for defaultValue. |
onSelectionChangedeprecated | (key: Key | null) => void | - | Deprecated compatibility alias for onChange. |
label | string | - | Visible label above the select. |
secondaryLabel | string | - | Secondary text shown next to the label. If not provided and isRequired is true, displays Required. |
description | string | - | Helper text displayed below the label. |
placeholder | string | Select an option | Text shown when no option is selected. |
size | smallmedium | small | Visual size of the select field. |
icon | ReactNode | - | Icon displayed before the selected value. |
searchabledeprecated | boolean | - | Deprecated compatibility prop for static option arrays. Use search instead. |
searchPlaceholderdeprecated | string | Search... | Deprecated compatibility prop for static option arrays. Use search.placeholder instead. |
isOpen | boolean | - | Controlled open state. Use with onOpenChange. |
defaultOpen | boolean | - | Initial open state for uncontrolled usage. |
onOpenChange | (isOpen: boolean) => void | - | Called when the dropdown opens or closes. |
isDisabled | boolean | - | Prevents user interaction when true. |
disabledKeys | Iterable<Key> | - | Keys of options that should be disabled. |
isRequired | boolean | - | Marks the field as required for form validation. |
isInvalid | boolean | - | Displays the select in an error state. |
name | string | - | Form field name for form submission. |
className | string | - | Additional CSS class name for custom styling. |
style | CSSProperties | - | Inline CSS styles object. |
Inherits all React Aria Select props.
The options prop accepts an array containing either of the following shapes.
Use id for new code. The deprecated value identity remains supported for
static arrays.
Option| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | Preferred unique identity for the option. Required for new option features and async option sources. |
valuedeprecated | string | - | Deprecated compatibility identity for static option arrays. Use id instead. |
label | string | - | Display text for the option. |
disabled | boolean | - | Whether the option is disabled. |
description | string | - | Secondary text displayed below the option label. Requires id instead of the deprecated value field. |
leadingIcon | ReactNode | - | Icon displayed before the option text. Requires id instead of the deprecated value field. |
OptionSection| Prop | Type | Default | Description |
|---|---|---|---|
title | string | - | Heading displayed above the grouped options. |
options | Option[] | - | Options nested inside the section. |
Low-level item wrapper for fully custom item content.
| Prop | Type | Default | Description |
|---|---|---|---|
id | Key | - | Item identity for static composition. Dynamic items receive the id from their source item. |
textValue | string | - | Plain text used for search, keyboard navigation, and accessibility. |
children | ReactNode(values: ListBoxItemRenderProps) => ReactNode | - | Custom item content. Use the render function to respond to item state. |
showSelectionIndicator | boolean | false | Uses the standard BUI selection indicator and item content layout. |
isDisabled | boolean | false | Whether the item is disabled. |
className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria ListBoxItem props.
Preset item with a title, optional description, and optional leading icon.
| Prop | Type | Default | Description |
|---|---|---|---|
id | Key | - | Item identity for static composition. Dynamic items receive the id from their source item. |
title | string | - | Primary item text. |
description | string | - | Secondary text displayed below the title. |
leadingIcon | ReactNode | - | Icon displayed before the item text. |
isDisabled | boolean | false | Whether the item is disabled. |
className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria ListBoxItem props.
Preset item with an avatar and profile name.
| Prop | Type | Default | Description |
|---|---|---|---|
id | Key | - | Item identity for static composition. Dynamic items receive the id from their source item. |
name | string | - | Profile name. |
src | string | - | Avatar image source. The avatar displays initials when omitted. |
isDisabled | boolean | false | Whether the item is disabled. |
className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria ListBoxItem props.
<Select
name="font"
label="Font Family"
description="Choose a font family for your document"
options={[ ... ]}
/><Flex>
<Select
size="small"
label="Font family"
options={[ ... ]}
/>
<Select
size="medium"
label="Font family"
options={[ ... ]}
/>
</Flex><Select
name="font"
label="Font Family"
icon={<RiCloudLine />}
options={[ ... ]}
/><Select
isDisabled
label="Font family"
options={[ ... ]}
/><Select
name="font"
label="Font Family"
placeholder="Select a font"
disabledKeys={['cursive', 'serif']}
options={[
{ id: 'sans', label: 'Sans-serif' },
{ id: 'serif', label: 'Serif' },
{ id: 'mono', label: 'Monospace' },
{ id: 'cursive', label: 'Cursive' },
]}
/>Pass search to use the default contains filter.
<Select
name="country"
label="Country"
search={{ placeholder: 'Search countries...' }}
options={[
{ id: 'us', label: 'United States' },
{ id: 'ca', label: 'Canada' },
{ id: 'uk', label: 'United Kingdom' },
{ id: 'fr', label: 'France' },
{ id: 'de', label: 'Germany' },
// ... more options
]}
/><Select
label="Owner"
options={ownerOptions}
search={{
placeholder: 'Search owners...',
filter: (option, query) =>
option.label.toLocaleLowerCase().startsWith(query.toLocaleLowerCase()),
}}
/><Select
name="options"
label="Select multiple options"
selectionMode="multiple"
options={[
{ id: 'option1', label: 'Option 1' },
{ id: 'option2', label: 'Option 2' },
{ id: 'option3', label: 'Option 3' },
{ id: 'option4', label: 'Option 4' },
]}
/>Combine search and multiple selection.
<Select
name="skills"
label="Skills"
search={{ placeholder: 'Filter skills...' }}
selectionMode="multiple"
options={[
{ id: 'react', label: 'React' },
{ id: 'typescript', label: 'TypeScript' },
{ id: 'javascript', label: 'JavaScript' },
{ id: 'python', label: 'Python' },
// ... more options
]}
/>Group options under section headings by passing objects with a title and a
nested options array.
<Select
name="font"
label="Font Family"
options={[
{
title: 'Serif Fonts',
options: [
{ id: 'times', label: 'Times New Roman' },
{ id: 'georgia', label: 'Georgia' },
{ id: 'garamond', label: 'Garamond' },
],
},
{
title: 'Sans-Serif Fonts',
options: [
{ id: 'arial', label: 'Arial' },
{ id: 'helvetica', label: 'Helvetica' },
{ id: 'verdana', label: 'Verdana' },
],
},
]}
/>Sections are preserved when filtering with search.
<Select
name="font"
label="Font Family"
search={{ placeholder: 'Search fonts...' }}
options={[
{
title: 'Serif Fonts',
options: [
{ id: 'times', label: 'Times New Roman' },
{ id: 'georgia', label: 'Georgia' },
],
},
{
title: 'Sans-Serif Fonts',
options: [
{ id: 'arial', label: 'Arial' },
{ id: 'helvetica', label: 'Helvetica' },
],
},
]}
/>Use item presets or low-level items for fixed rich rows, or pass domain objects
through items with a child render function. Pass dependencies when that
render function also depends on values outside each item. Presets include the
standard BUI selection indicator. Low-level items own their internal layout and
selection treatment; set showSelectionIndicator to use the standard
indicator and content layout.
import { Select, SelectItem } from '@backstage/ui';
<Select label="Release channel" items={releaseChannels}>
{channel => (
<SelectItem textValue={channel.name} showSelectionIndicator>
<CustomReleaseChannel channel={channel} />
</SelectItem>
)}
</Select>Import useAsyncList from @backstage/ui and pass its result directly to
options or items. With server search, query state, loading state, and
incremental loading are derived from the list. With client search, Select
filters the currently loaded items on the client.
import { Select, useAsyncList } from '@backstage/ui';
const owners = useAsyncList({
async load({ signal, filterText, cursor }) {
return fetchOwners({ signal, query: filterText, cursor });
},
});
<Select
label="Owner"
options={owners}
search={{ mode: 'server', placeholder: 'Search owners...' }}
/>If data fetching is managed elsewhere, control the server query through
search and pass loading with the current state and optional load-more
callback.
<Select
label="Owner"
options={results}
search={{
mode: 'server',
inputValue: query,
onInputChange: setQuery,
}}
loading={{ state: isLoading ? 'filtering' : 'idle', onLoadMore }}
/>Size can change at different breakpoints.
<Select
size={{ initial: 'small', lg: 'medium' }}
label="Font family"
options={[ ... ]}
/>Our theming system is based on a mix between CSS classes, CSS variables and data attributes. If you want to customise this component, you can use one of these class names below.
bui-Selectbui-SelectPopoverBreaking Select now supports async collections, incremental loading, client and server search, and rich or custom item rendering. Loading placeholders expose .bui-SelectLoading and .bui-SelectLoadingRow, and stale retained results expose data-stale on .bui-SelectList.
The public SelectProps interface is now a union type, and Select popover list content is no longer a direct child of .bui-SelectPopover. #34489
Migration Guide:
Required on upgrade:
Replace interfaces that extend SelectProps with type intersections.
- interface MySelectProps extends SelectProps {
- trackingId: string;
- }
+ type MySelectProps = SelectProps & {
+ trackingId: string;
+ };
Update CSS selectors that rely on list content being a direct child of .bui-SelectPopover. Select popovers now use the standard BUI Popover content structure, with contents wrapped in .bui-Box.bui-PopoverContent. The existing .bui-Popover.bui-SelectPopover root classes are unchanged.
Optional migration away from deprecated APIs:
Prefer id instead of value for plain options. Existing array-valued options using value remain supported as a deprecated compatibility path, but new option content fields and async option sources require id.
Replace searchable and searchPlaceholder with nested search configuration:
- <Select searchable searchPlaceholder="Search owners" />
+ <Select search={{ placeholder: 'Search owners' }} />
Fixed async pagination in Combobox and Select popovers so additional pages load as users scroll instead of loading every page immediately. Combobox now uses .bui-PopoverContent as its scroll container, while all Select variants use the new .bui-SelectResults results container.
Searchable Select keeps its search field fixed while results scroll. The new public classes .bui-SelectContent and .bui-SelectResults expose this layout for theme customization. #34591
title and a nested options array alongside (or instead of) regular options to render grouped dropdowns with section headers. #34012aria-describedby, making them accessible to screen readers. Added a descriptionSlot prop to FieldLabel that uses React Aria's slot mechanism to automatically wire up the connection. #33817Migrated all components from useStyles to useDefinition hook. Exported OwnProps types for each component, enabling better type composition for consumers. #33050
Fixed focus ring styles to use React Aria's [data-focus-visible] data attribute instead of the native CSS :focus-visible pseudo-class. This ensures keyboard focus rings render reliably when focus is managed programmatically by React Aria (e.g. inside a GridList, Menu, or Select). #33358
Fixed scroll overflow in Menu and Select popover content when constrained by viewport height. #33049
Fixed focus-visible outline styles for Menu and Select components. #32983
The Select trigger now automatically adapts its background colour based on the parent background context. #33102
aria-label attributes to SearchField components in Select, MenuAutocomplete, and MenuAutocompleteListbox to fix accessibility warnings. #32337Breaking The SelectProps interface now accepts a generic type parameter for selection mode.
Added searchable and multiple selection support to Select component. The component now accepts searchable, selectionMode, and searchPlaceholder props to enable filtering and multi-selection modes. #31649
Migration Guide:
If you're using SelectProps type directly, update from SelectProps to SelectProps<'single' | 'multiple'>. Component usage remains backward compatible.