Sikau

React patterns at scale · Day 10 · about 50 min

Capstone: An accessible autocomplete

A cached, race-free, keyboard-friendly combobox.

The hardest small component

An autocomplete looks like an input and a list. Underneath, it's one of the hardest widgets on the web: requests that come back out of order, results worth caching, a list that must open and close at the right moments, and keyboard and screen-reader support that most implementations get wrong. That makes it the perfect capstone — it needs the patterns from this whole course at once.

You'll build a combobox following the WAI-ARIA Authoring Practices: a reducer for the UI state (lesson 6), a cache for results (lesson 7), an effect that can't suffer a race condition, and the ARIA attributes that let everyone use it. Then you'll see how teams test a component like this the way users use it.

The combobox pattern

Focus stays in the input the whole time. Arrow keys move a *virtual* highlight through the options, and the input announces which option is active with aria-activedescendant, pointing at that option's id. Screen readers read the active option as if it had focus, while the user keeps typing.

<input
  role="combobox"
  aria-expanded={showList}
  aria-controls="results"
  aria-autocomplete="list"
  aria-activedescendant={active >= 0 ? "opt-" + active : undefined}
/>
<ul id="results" role="listbox">
  <li id="opt-0" role="option" aria-selected={active === 0}>Momo</li>
</ul>
  • ArrowDown / ArrowUp move the highlight and wrap at the ends; call preventDefault() so the caret doesn't jump.
  • Enter picks the highlighted option, fills the input and closes the list.
  • Escape closes the list and keeps what was typed.
  • Pick with onMouseDown + preventDefault() so clicking an option doesn't blur the input first.
  • Give the input a visible <label> — placeholder text isn't a label.

The rest of this lesson

  • Races, caching and testingexplain
  • Out of orderpredict
  • Keys and attributesmatch
  • Testing like a userorder
  • Build the autocompletechallenge
  • Badge quizquiz
  • What you learnedrecap

Continue this lesson free — run real code in your browser

Continue with Google

See all 10 lessons of React patterns at scale