rc

Autocomplete

An autocomplete allows users to search or filter a list of suggestions.

Theme 
disableAutoFocusFirst 
Example
CommandPalette.tsx
CommandPalette.css
import {CommandPalette} from './CommandPalette';
import {MenuItem, Text} from './Menu';
import {Button} from './Button';
import {FilePlus2, FolderPlus, User, UserPen, CircleDotDashed, ChartPie, Tag} from 'lucide-react';
import {DialogTrigger} from 'react-aria-components/Dialog';
import {useState} from 'react';

function Example(props) {
  let [isOpen, setOpen] = useState(false);
  return (
    <DialogTrigger isOpen={isOpen} onOpenChange={setOpen}>
      <Button>Open Command Palette <kbd>⌘ J</kbd></Button>
      <CommandPalette
        {...props}
        isOpen={isOpen}
        onOpenChange={setOpen}>
        <MenuItem textValue="Create new file...">
          <FilePlus2 />
          <Text>Create new file...</Text>
        </MenuItem>
        <MenuItem textValue="Create new folder...">
          <FolderPlus />
          <Text>Create new folder...</Text>
        </MenuItem>
        <MenuItem textValue="Assign to...">
          <UserPen />
          <Text>Assign to...</Text>
        </MenuItem>
        <MenuItem textValue="Assign to me">
          <User />
          <Text>Assign to me</Text>
        </MenuItem>
        <MenuItem textValue="Change status...">
          <CircleDotDashed />
          <Text>Change status...</Text>
        </MenuItem>
        <MenuItem textValue="Change priority...">
          <ChartPie />
          <Text>Change priority...</Text>
        </MenuItem>
        <MenuItem textValue="Add label...">
          <Tag />
          <Text>Add label...</Text>
        </MenuItem>
        <MenuItem textValue="Remove label...">
          <Tag />
          <Text>Remove label...</Text>
        </MenuItem>
      </CommandPalette>
    </DialogTrigger>
  );
}

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.

disableVirtualFocus 
disableAutoFocusFirst 
import {Autocomplete, useFilter} from 'react-aria-components/Autocomplete';
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Button} from './Button';
import {SearchField} from './SearchField';

function Example(props) {
  let {contains} = useFilter({sensitivity: 'base'});

  return (
    <MenuTrigger>
      <Button>Add tag...</Button>
      <div style={{display: 'flex', flexDirection: 'column', maxHeight: 'inherit'}}>
        <Autocomplete {...props} filter={contains}>
          <SearchField
            autoFocus
            aria-label="Search tags"
            placeholder="Search tags"
            style={{margin: 4}} />
          <Menu style={{flex: 1}} renderEmptyState={() => 'No results.'}>
            <MenuItem>News</MenuItem>
            <MenuItem>Travel</MenuItem>
            <MenuItem>Shopping</MenuItem>
            <MenuItem>Business</MenuItem>
            <MenuItem>Entertainment</MenuItem>
            <MenuItem>Food</MenuItem>
            <MenuItem>Technology</MenuItem>
            <MenuItem>Health</MenuItem>
            <MenuItem>Science</MenuItem>
          </Menu>
        </Autocomplete>
      </div>
    </MenuTrigger>
  );
}

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.

No results found.
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

NameTypeDefault
filter( textValue: string, inputValue: string, node: <T> ) => booleanDefault:

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.

disableAutoFocusFirstbooleanDefault: 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.

disableVirtualFocusbooleanDefault: false

Whether the autocomplete should disable virtual focus, instead making the wrapped collection directly tabbable.

childrenReactNodeDefault:

The children wrapped by the autocomplete. Consists of at least an input element and a collection element to filter.

inputValuestringDefault:

The value of the autocomplete input (controlled).

defaultInputValuestringDefault:

The default value of the autocomplete input (uncontrolled).

onInputChange(value: string) => voidDefault:

Handler that is called when the autocomplete input value changes.