A text input paired with a filterable dropdown for selecting or typing a value.
import { Combobox } from '@backstage/ui';
<Combobox
name="font"
label="Font Family"
options={[
{ id: 'sans', label: 'Sans-serif' },
{ id: 'serif', label: 'Serif' },
{ id: 'mono', label: 'Monospace' },
{ id: 'cursive', label: 'Cursive' },
]}
/>Provide a visible label whenever possible, or use aria-label or
aria-labelledby when the field is labeled elsewhere.
Combobox 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 ComboboxItemText and ComboboxItemProfile for standard rich rows. Use
the low-level ComboboxItem for fully custom content, and provide its
textValue for filtering and accessibility.
Option sections are supported only in static options arrays.
Combobox filters loaded items with a client-side contains match by default.
Pass search.filter to match against the full option or item. Custom filters
require an options or items source; static composition uses the default
text-value match. Pass search={{ mode: 'server' }} with a useAsyncList
source to forward the input query to the source instead. To control a client
query, pass search.inputValue together with search.onInputChange. Use
search.defaultInputValue to set an initial uncontrolled query.
Regular collections use item keys for value, defaultValue, and onChange.
Direct async server collections use the full option or item instead. This lets
the Combobox retain the selected item while the current server results change.
Custom async server items require a canonical textValue; plain options use
their label.
While the user edits a direct async server Combobox, the selected item remains
committed. Leaving the field restores its canonical text and queries the source
with that text. With allowsCustomValue, unmatched text remains in the input
and no option is selected.
If server data is managed elsewhere, control search.inputValue and
search.onInputChange yourself. This manual mode keeps key-based selection.
Pass loading to report loading state and optionally load more results.
In server mode, direct useAsyncList sources provide their own query and
loading state and cannot be combined with these manual props.
| 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 | trueComboboxSearch<T>ComboboxAsyncSearch<T> | - | Configures client or server search behavior. Omit it to filter loaded items with the default contains match. |
loading | { state: LoadingState; onLoadMore?: () => void } | - | Manual loading state for non-async collections. Async sources provide this automatically. |
allowsCustomValue | boolean | false | Allows unmatched text to remain in the input when committed. No option is selected for the custom text. |
value | KeyTnull | - | Controlled selection. Regular collections use an item key; direct async server collections use the full selected item. |
defaultValue | KeyTnull | - | Initial uncontrolled selection. Regular collections use an item key; direct async server collections use the full selected item. |
onChange | (value: Key | null) => void(item: T | null) => void | - | Called when selection changes. Direct async server collections emit the full selected item. |
selectedKeydeprecated | Key | - | Deprecated compatibility alias for value. |
defaultSelectedKeydeprecated | Key | - | Deprecated compatibility alias for defaultValue. |
onSelectionChangedeprecated | (key: Key | null) => void | - | Deprecated compatibility alias for onChange. |
inputValuedeprecated | string | - | Deprecated compatibility prop for static option arrays. Use search.inputValue instead. |
defaultInputValuedeprecated | string | - | Deprecated compatibility prop for static option arrays. Use search.defaultInputValue instead. |
onInputChangedeprecated | (value: string) => void | - | Deprecated compatibility prop for static option arrays. Use search.onInputChange instead. |
defaultFilterdeprecated | (textValue: string, inputValue: string) => boolean | - | Deprecated compatibility prop for static option arrays. Use search.filter instead. |
label | string | - | Visible label above the combobox. |
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 | - | Text shown when the input is empty. |
size | smallmedium | small | Visual size of the combobox field. |
icon | ReactNode | - | Icon displayed before the input. |
onOpenChange | (isOpen: boolean) => void | - | Called when the dropdown opens or closes. |
isOpen | boolean | - | Controlled open state. Use with onOpenChange. |
defaultOpen | boolean | - | Initial open state for uncontrolled usage. |
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 combobox 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 ComboBox 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 filtering, 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.
<Combobox
name="font"
label="Font Family"
description="Choose a font family for your document"
options={[ ... ]}
/>Pass search.filter to replace the default contains match.
<Combobox
label="Owner"
options={ownerOptions}
search={{
filter: (option, query) =>
option.label.toLocaleLowerCase().startsWith(query.toLocaleLowerCase()),
}}
/>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 { Combobox, ComboboxItemProfile } from '@backstage/ui';
<Combobox label="Owner" items={owners}>
{owner => (
<ComboboxItemProfile
name={owner.name}
src={owner.avatarUrl}
/>
)}
</Combobox>Import useAsyncList from @backstage/ui and pass its result directly to
options or items. The Combobox derives query state, loading state, and
incremental loading from the list.
Direct async server collections use the full selected option or item for
value, defaultValue, and onChange. Custom items need an id and a
canonical textValue; plain options use their label. The selected item
remains committed while the user edits the query. Leaving the field restores
its canonical text unless allowsCustomValue keeps unmatched text.
import { useState } from 'react';
import { Combobox, ComboboxItemProfile, useAsyncList } from '@backstage/ui';
type Owner = {
id: string;
textValue: string;
name: string;
avatarUrl?: string;
};
const owners = useAsyncList<Owner>({
async load({ signal, filterText, cursor }) {
return fetchOwners({ signal, query: filterText, cursor });
},
});
const [owner, setOwner] = useState<Owner | null>(null);
<Combobox
label="Owner"
items={owners}
placeholder="Search or select an owner"
search={{ mode: 'server' }}
value={owner}
onChange={setOwner}
>
{owner => <ComboboxItemProfile name={owner.name} src={owner.avatarUrl} />}
</Combobox>If data fetching is managed elsewhere, control the server query through
search and pass loading with the current state and optional load-more
callback. This manual mode keeps the regular key-based selection API.
<Combobox
label="Owner"
options={results}
search={{
mode: 'server',
inputValue: query,
onInputChange: setQuery,
}}
loading={{ state: isLoading ? 'filtering' : 'idle', onLoadMore }}
/><Flex>
<Combobox
size="small"
label="Font family"
options={[ ... ]}
/>
<Combobox
size="medium"
label="Font family"
options={[ ... ]}
/>
</Flex><Combobox
name="font"
label="Font Family"
icon={<RiCloudLine />}
options={[ ... ]}
/><Combobox
isDisabled
label="Font family"
options={[ ... ]}
/><Combobox
name="font"
label="Font Family"
placeholder="Pick a font"
disabledKeys={['cursive', 'serif']}
options={[
{ id: 'sans', label: 'Sans-serif' },
{ id: 'serif', label: 'Serif' },
{ id: 'mono', label: 'Monospace' },
{ id: 'cursive', label: 'Cursive' },
]}
/>Set allowsCustomValue to keep text that does not match an option. Custom text
does not create a selected option.
<Combobox
name="country"
label="Country"
allowsCustomValue
placeholder="Type any country"
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
]}
/>Group options under section headings by passing objects with a title and a
nested options array.
<Combobox
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' },
],
},
]}
/>Size can change at different breakpoints.
<Combobox
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-Comboboxbui-ComboboxPopoverBreaking Combobox now supports async collections, incremental loading, client and server search, and rich or custom item rendering. Loading placeholders expose .bui-ComboboxLoading and .bui-ComboboxLoadingRow, and stale visible results expose data-stale on .bui-ComboboxList.
The public ComboboxProps interface is now a union type. #34489
Migration Guide:
Required on upgrade:
Replace interfaces that extend ComboboxProps with type intersections.
- interface MyComboboxProps extends ComboboxProps {
- trackingId: string;
- }
+ type MyComboboxProps = ComboboxProps & {
+ trackingId: string;
+ };
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.
Move input state and custom filtering into the nested search configuration:
- <Combobox inputValue={query} onInputChange={setQuery} />
+ <Combobox search={{ inputValue: query, onInputChange: setQuery }} />
The existing top-level input state props remain supported as a deprecated compatibility path for plain-array options.
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
Fixed Combobox client search crashing when used with plain options. #34590
Combobox component. It pairs a text input with a filterable dropdown of options and supports single selection, sectioned options, icons, sizes, and custom typed values via allowsCustomValue. #34118