An autocomplete allows users to search or filter a list of suggestions.
Vanilla CSS theme
--tint CSS variable used by the Vanilla CSS examples.Content
Autocomplete filters a collection component using a TextField or SearchField. It can be used to build UI patterns such as command palettes, searchable menus, filterable selects, and more.
Menu and ListBox support virtual focus, which allows arrow key navigation within the list while the text input is focused. Use disableVirtualFocus to require the user to tab between the input and list.
Asynchronous loading
When the filter prop is not set, the items are controlled. This example uses a backend API to perform searching instead of filtering a static list on the client.
import {Autocomplete} from 'react-aria-components/Autocomplete';
import {useAsyncList} from 'react-aria-components/useAsyncList';
import {SearchField} from './SearchField';
import {ListBox, ListBoxItem} from './ListBox';
function AsyncLoadingExample() {
let list = useAsyncList<{name: string}>({
async load({signal, filterText}) {
let res = await fetch(
`https://swapi.py4e.com/api/people/?search=${filterText}`,
{signal}
);
let json = await res.json();
return {
items: json.results
};
}
});
return (
<Autocomplete
inputValue={list.filterText}
onInputChange={list.setFilterText}>
<SearchField
label="Search Star Wars Characters"
placeholder="Search"
style={{width: 250, margin: 8}} />
<ListBox
items={list.items}
selectionMode="multiple"
renderEmptyState={() => 'No results found.'}
style={{height: 300}}>
{(item) => <ListBoxItem id={item.name}>{item.name}</ListBoxItem>}
</ListBox>
</Autocomplete>
);
}
Inline completions
Set the Autocomplete inputValue to a substring of the full input value to enable inline completions such as @mentions. Completions can be displayed in a popover by controlling its isOpen state. For inline suggestions, use the getTargetRect prop to position the popover relative to the anchor character.
import {Autocomplete, useFilter} from 'react-aria-components/Autocomplete';
import {TextArea} from './TextField';
import {Popover} from './Popover';
import {Menu, MenuItem} from './Menu';
import {useState, useRef} from 'react';
import {flushSync} from 'react-dom';
import getCaretRect from 'textarea-caret';
function Example() {
let {startsWith} = useFilter({sensitivity: 'base'});
let [inputValue, setInputValue] = useState('');
let [anchorIndex, setAnchorIndex] = useState(-1);
let [filterValue, setFilterValue] = useState('');
let inputRef = useRef<HTMLTextAreaElement>(null);
let updateFilter = () => {
let {selectionStart, selectionEnd, value} = inputRef.current!;
if (selectionStart === selectionEnd && document.activeElement === inputRef.current!) {
// The current filter value is the substring between
// the anchor character '@' and the caret position.
let index = value.lastIndexOf('@', selectionStart);
if (index >= 0) {
let slice = value.slice(index + 1, selectionStart);
// Spaces are not allowed in the filter value.
if (!slice.includes(' ')) {
setAnchorIndex(index);
setFilterValue(slice);
return;
}
}
}
// Reset the anchor index, but not the filter value so
// that the menu does not flicker during the close animation.
setAnchorIndex(-1);
};
return (
// Pass the filter substring to Autocomplete.
<Autocomplete inputValue={filterValue} filter={startsWith}>
<TextArea
label="Comment"
placeholder="Type @ for autocomplete"
style={{width: '100%'}}
// Pass the full input value to the TextArea.
value={inputValue}
onChange={value => {
setInputValue(value);
updateFilter();
}}
onSelect={updateFilter}
onBlur={updateFilter}
inputRef={inputRef} />
<Popover
triggerRef={inputRef}
// Open the popover when there is an active anchor character.
isOpen={anchorIndex >= 0}
isNonModal
placement="bottom start"
trigger="MenuTrigger"
// Calculate the position of the popover relative to the anchor character.
getTargetRect={target => {
let {top, left} = getCaretRect(inputRef.current!, anchorIndex!);
let {top: targetTop, left: targetLeft} = target.getBoundingClientRect();
return new DOMRect(targetLeft + left, targetTop + top, 1, 16);
}}>
<Menu
items={usernames}
renderEmptyState={() => 'No results found.'}
onAction={value => {
// Insert the completion at the anchor index and update the caret position.
let prefix = inputValue.slice(0, anchorIndex!) + '@' + value + ' ';
let suffix = inputValue.slice(inputRef.current!.selectionEnd!);
flushSync(() => setInputValue(prefix + suffix));
inputRef.current!.setSelectionRange(prefix.length, prefix.length);
updateFilter();
}}>
{(item) => <MenuItem id={item}>{item}</MenuItem>}
</Menu>
</Popover>
</Autocomplete>
);
}
Examples
API
<Autocomplete>
<SearchField /> or <TextField />
<Menu />, <ListBox />, <TagGroup />, <GridList />, or <Table />
</Autocomplete>
Autocomplete
| Name | Type | Default |
|---|---|---|
filter | (
textValue: string,
inputValue: string,
node: Node | Default: — |
An optional filter function used to determine if a option should be included in the autocomplete list. Include this if the items you are providing to your wrapped collection aren't filtered by default. | ||
disableAutoFocusFirst | boolean | Default: false
|
Whether or not to focus the first item in the collection after a filter is performed. Note this
is only applicable if virtual focus behavior is not turned off via | ||
disableVirtualFocus | boolean | Default: false
|
Whether the autocomplete should disable virtual focus, instead making the wrapped collection directly tabbable. | ||
children | ReactNode | Default: — |
The children wrapped by the autocomplete. Consists of at least an input element and a collection element to filter. | ||
inputValue | string | Default: — |
The value of the autocomplete input (controlled). | ||
defaultInputValue | string | Default: — |
The default value of the autocomplete input (uncontrolled). | ||
onInputChange | | Default: — |
Handler that is called when the autocomplete input value changes. | ||