Downshift

An autocomplete is a form control the platform does not provide, and the WAI-ARIA combobox pattern it must follow is long: roles on four elements, aria-expanded, aria-activedescendant, arrow-key and Escape handling, focus that never leaves the input. Downshift 12,303 (github.com/downshift-js/downshift (https://github.com/downshift-js/downshift 12,303 ), MIT, npm 2,036 install downshift, 9.4.0) implements that logic and renders nothing. Its Hooks — useCombobox, useSelect and useMultipleSelection — return prop getters you spread onto your own markup, so the styling stays yours.

An accessible combobox with useComboboxJavaScript
import { useState } from 'react';
import { useCombobox } from 'downshift';
const fruits = ['Apple', 'Apricot', 'Banana', 'Blueberry', 'Cherry'];
export default function FruitPicker() {
  const [items, setItems] = useState(fruits);
  const cb = useCombobox({ items, onInputValueChange: ({ inputValue }) =>
    setItems(fruits.filter(f => f.toLowerCase().startsWith(inputValue.toLowerCase()))) });
  return (
    <div>
      <label {...cb.getLabelProps()}>Fruit</label>
      <input {...cb.getInputProps()} />
      <ul {...cb.getMenuProps()}>
        {cb.isOpen && items.map((item, index) => (
          <li key={item} {...cb.getItemProps({ item, index })}
              aria-current={cb.highlightedIndex === index}>{item}</li>))}
      </ul></div>);
}

Spread every getter on the element it names: getMenuProps must land on the <ul> even when the menu is closed, or Downshift warns and screen readers lose the relationship to the input. Pass your own handler through the getter — getInputProps({ onKeyDown: mine }) — so Downshift runs its handler first and yours second. Use itemToString when the items are objects, and capture the choice with onSelectedItemChange.