Skip to content

AutocompleteInput

Search-as-you-type field for selecting a single item from a search source.

function AutocompleteInputStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
<AutocompleteInputStory {...args} />
function CustomItemsStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem<{role: string}> | null>(
null,
);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
renderItem={(item, query) => (
<AutocompleteInputItem
description={item.auxiliaryData?.role}
icon={User}
item={item}
query={query}
/>
)}
searchSource={source}
value={value}
/>
);
}
<CustomItemsStory {...args} />
function DisabledResultsStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem<{role: string}> | null>(
null,
);
const source = useMemo(
() => createStaticSearchSource(peopleWithDisabledResult),
[],
);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
renderItem={(item, query) => (
<AutocompleteInputItem
description={item.auxiliaryData?.role}
item={item}
query={query}
/>
)}
searchSource={source}
value={value}
/>
);
}
<DisabledResultsStory {...args} />
function AutocompleteInputStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
<AutocompleteInputStory {...args} />
function AutocompleteInputStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
<AutocompleteInputStory {...args} />
function AutocompleteInputStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
<AutocompleteInputStory {...args} />
(args: AutocompleteInputProps) => {
const [value, setValue] = useState<SearchableItem<{role: string}> | null>(
people[1],
);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
(args: AutocompleteInputProps) => {
const [value, setValue] = useState<SearchableItem<{role: string}> | null>(
people[0],
);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
(args: AutocompleteInputProps) => {
const source = useMemo(() => createStaticSearchSource(people), []);
const [small, setSmall] = useState<SearchableItem | null>(null);
const [medium, setMedium] = useState<SearchableItem | null>(null);
const [large, setLarge] = useState<SearchableItem | null>(null);
return (
<div style={{display: 'grid', gap: 16}}>
<AutocompleteInput
{...args}
debounceMs={0}
label="Small"
onChange={setSmall}
searchSource={source}
size="sm"
value={small}
/>
<AutocompleteInput
{...args}
debounceMs={0}
label="Medium"
onChange={setMedium}
searchSource={source}
value={medium}
/>
<AutocompleteInput
{...args}
debounceMs={0}
label="Large"
onChange={setLarge}
searchSource={source}
size="lg"
value={large}
/>
</div>
);
}
(args: AutocompleteInputProps) => {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createAsyncSource(manyPeople), []);
return (
<AutocompleteInput
{...args}
debounceMs={200}
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
(args: AutocompleteInputProps) => {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo<SearchSource<SearchableItem>>(
() => ({
bootstrap: () => [],
search: async () => Promise.reject(new Error('Network error')),
}),
[],
);
return (
<AutocompleteInput
{...args}
debounceMs={0}
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
function AutocompleteInputStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
<AutocompleteInputStory {...args} />
Search by name or role.
function AutocompleteInputStory(
args: React.ComponentProps<typeof AutocompleteInput>,
) {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
<AutocompleteInputStory {...args} />
() => {
const [value, setValue] = useState<SearchableItem | null>(null);
const [query, setQuery] = useState('');
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<BaseAutocompleteInput
debounceMs={0}
hasEntriesOnFocus
onChange={setValue}
onQueryChange={setQuery}
placeholder="Search people"
query={query}
searchSource={source}
value={value}
/>
);
}
(args: AutocompleteInputProps) => {
const [value, setValue] = useState<SearchableItem | null>(null);
const source = useMemo(() => createStaticSearchSource(manyPeople), []);
return (
<AutocompleteInput
{...args}
debounceMs={0}
hasEntriesOnFocus
maxMenuItems={12}
onChange={setValue}
searchSource={source}
value={value}
/>
);
}
({
isOptional: _io,
isRequired: _ir,
...args
}: AutocompleteInputProps) => {
const source = useMemo(() => createStaticSearchSource(people), []);
const [required, setRequired] = useState<SearchableItem | null>(null);
const [optional, setOptional] = useState<SearchableItem | null>(null);
return (
<div style={{display: 'grid', gap: 16}}>
<AutocompleteInput
{...args}
debounceMs={0}
isRequired
label="Required assignee"
onChange={setRequired}
searchSource={source}
value={required}
/>
<AutocompleteInput
{...args}
debounceMs={0}
isOptional
label="Optional reviewer"
onChange={setOptional}
searchSource={source}
value={optional}
/>
</div>
);
}
<AutocompleteInput
{...args}
onChange={() => {}}
searchSource={createStaticSearchSource(people)}
value={people[0]}
/>
Focus to show in-memory suggestions immediately.
Focus to load suggestions after a one-second delay.
function InitialSuggestionsComparisonStory(args: AutocompleteInputProps) {
const [syncValue, setSyncValue] = useState<SearchableItem | null>(null);
const [asyncValue, setAsyncValue] = useState<SearchableItem | null>(null);
const syncSource = useMemo(() => createStaticSearchSource(people), []);
const asyncSource = useMemo(() => {
const source = createAsyncSource(people, 1000);
return {...source, bootstrap: async () => source.search('')};
}, []);
return (
<VStack gap={4}>
<AutocompleteInput
{...args}
description="Focus to show in-memory suggestions immediately."
hasEntriesOnFocus
label="Synchronous initial suggestions"
onChange={setSyncValue}
searchSource={syncSource}
value={syncValue}
/>
<AutocompleteInput
{...args}
description="Focus to load suggestions after a one-second delay."
hasEntriesOnFocus
label="Asynchronous initial suggestions"
onChange={setAsyncValue}
searchSource={asyncSource}
value={asyncValue}
/>
</VStack>
);
}
<InitialSuggestionsComparisonStory {...args} />

Search-as-you-type field for selecting a single item from a search source.

When isOptional: false, isRequired: false

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the input wrapper.
data-testidstring—Test ID applied to the input wrapper.
debounceMsnumber150Debounce delay in milliseconds before search runs.
descriptionReactNode—Supporting text rendered below the label.
emptySearchResultsTextstring'No results found'Empty state text.
errorTextstring'Something went wrong'Text shown in the menu when a search fails.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasClearbooleantrueWhether to show a clear button when a value is selected.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
label*string—Field label.
labelEndReactNode—Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip content shown next to the label.
maxMenuItemsnumber10Maximum number of menu items.
onChange*(item: T | null) => void—Called when selection changes.
onOpenChange(isOpen: boolean) => void—Called when the result popover opens or closes.
onQueryChange(query: string) => void—Called when the query changes.
placeholderstring—Placeholder text.
refRef<HTMLDivElement>—Ref forwarded to the field root.
renderItem(item: T, query: string) => ReactNode—Custom result renderer. Receives the item and active query.
searchSource*SearchSource<T>—Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
startIconIconComponent—Icon shown before the input.
statusInputStatus—Validation status displayed below the selector.
styleCSSProperties—Inline styles applied to the input wrapper.
value*T | null—Selected item.
isOptionalfalse—
isRequiredfalse—

When isOptional: true, isRequired: false

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the input wrapper.
data-testidstring—Test ID applied to the input wrapper.
debounceMsnumber150Debounce delay in milliseconds before search runs.
descriptionReactNode—Supporting text rendered below the label.
emptySearchResultsTextstring'No results found'Empty state text.
errorTextstring'Something went wrong'Text shown in the menu when a search fails.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasClearbooleantrueWhether to show a clear button when a value is selected.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
label*string—Field label.
labelEndReactNode—Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip content shown next to the label.
maxMenuItemsnumber10Maximum number of menu items.
onChange*(item: T | null) => void—Called when selection changes.
onOpenChange(isOpen: boolean) => void—Called when the result popover opens or closes.
onQueryChange(query: string) => void—Called when the query changes.
placeholderstring—Placeholder text.
refRef<HTMLDivElement>—Ref forwarded to the field root.
renderItem(item: T, query: string) => ReactNode—Custom result renderer. Receives the item and active query.
searchSource*SearchSource<T>—Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
startIconIconComponent—Icon shown before the input.
statusInputStatus—Validation status displayed below the selector.
styleCSSProperties—Inline styles applied to the input wrapper.
value*T | null—Selected item.
isOptional*true—
isRequiredfalse—

When isOptional: false, isRequired: true

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the input wrapper.
data-testidstring—Test ID applied to the input wrapper.
debounceMsnumber150Debounce delay in milliseconds before search runs.
descriptionReactNode—Supporting text rendered below the label.
emptySearchResultsTextstring'No results found'Empty state text.
errorTextstring'Something went wrong'Text shown in the menu when a search fails.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasClearbooleantrueWhether to show a clear button when a value is selected.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isLabelHiddenbooleanfalseWhether to visually hide the label.
label*string—Field label.
labelEndReactNode—Content shown at the end of the label row, such as a link. It renders outside the label, so it does not focus the control or change its accessible name. It stays visible when isLabelHidden is true.
labelIconIconComponent—Icon shown before the label.
labelTooltipReactNode—Tooltip content shown next to the label.
maxMenuItemsnumber10Maximum number of menu items.
onChange*(item: T | null) => void—Called when selection changes.
onOpenChange(isOpen: boolean) => void—Called when the result popover opens or closes.
onQueryChange(query: string) => void—Called when the query changes.
placeholderstring—Placeholder text.
refRef<HTMLDivElement>—Ref forwarded to the field root.
renderItem(item: T, query: string) => ReactNode—Custom result renderer. Receives the item and active query.
searchSource*SearchSource<T>—Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
startIconIconComponent—Icon shown before the input.
statusInputStatus—Validation status displayed below the selector.
styleCSSProperties—Inline styles applied to the input wrapper.
value*T | null—Selected item.
isOptionalfalse—
isRequired*true—

Internal combobox engine used by AutocompleteInput and TagsInput.

PropTypeDefaultDescription
anchorRefRefObject<HTMLElement | null>—Ref to the element the result popover should align to.
ariaDescribedBystring—IDs describing the input.
ariaLabelstring—Accessible name for the input. Use when no visible <label> is tied to it, such as inside an InputGroup where the group owns the label.
classNamestring—Additional CSS class names applied to the input.
data-testidstring—Test ID applied to the input element.
debounceMsnumber150Debounce delay in milliseconds before search runs.
emptySearchResultsTextstring'No results found'Empty state text.
errorTextstring'Something went wrong'Text shown in the menu when a search fails.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
hasReopenOnSelectbooleanfalseWhether to re-bootstrap results after selecting an item. Useful for multi-select comboboxes where the user picks several items in a row.
inputIdstring—Optional ID for the input.
isDisabledbooleanfalseWhether the input is disabled.
isReadOnlybooleanfalseWhether the value is displayed without allowing focus or interaction.
isRequiredbooleanfalseWhether the input is required.
maxMenuItemsnumber10Maximum number of menu items.
onChange*(item: T | null) => void—Called when a result is selected.
onKeyDown(event: KeyboardEvent<HTMLInputElement>) => void—Keyboard handler invoked before internal navigation.
onOpenChange(isOpen: boolean) => void—Called when the result popover opens or closes.
onQueryChange*(query: string) => void—Called when the query changes.
placeholderstring'Search...'Placeholder text.
query*string—Current query string.
refRef<HTMLInputElement>—Ref forwarded to the input.
renderItem(item: T) => ReactNode—Custom result renderer.
searchSource*SearchSource<T>—Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
styleCSSProperties—Inline styles applied to the input.
value*T | null—Selected item.

Default layout for AutocompleteInput and TagsInput result rows. When the item has a pre-rendered `element`, it is returned directly. Otherwise the component renders a flex row with an optional icon, primary label, and description.

When Variant 1

PropTypeDefaultDescription
item*SearchableItem & {element: ReactNode}—Search result item with custom element content.
querystring | string[]—Literal query or queries highlighted in the item label.

When Variant 2

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the item layout.
data-testidstring—Test ID applied to the item layout.
descriptionReactNode—Supporting text displayed below the label.
iconIconComponent—Icon or avatar rendered before the label.
item*SearchableItem & {element?: undefined}—Search result item without a custom element.
refRef<HTMLDivElement>—Ref forwarded to the item layout.
styleCSSProperties—Inline styles applied to the item layout.
querystring | string[]—Literal query or queries highlighted in the item label.