AniUI Academy

Build an autocomplete

mediumUI ComponentsHigh freq+200 XP

Build a search box for a small, fixed list of fruit. Typing filters the list to items whose name starts with what has been typed so far, case-insensitively.

Picking a suggestion fills the input with it and closes the list.

Examples

// Type "ap" into the input
// → <li>Apple</li>, <li>Apricot</li>

// Type "zzz"
// → the <ul> is empty (no "no results" row)

// Click <li>Apple</li>
// → input value is "Apple", no <li> rendered

Prefix match, case-insensitive; no stale list once a suggestion is picked.

const suggestions =
  query.length === 0 || selected
    ? []
    : FRUITS.filter((f) =>
        f.toLowerCase().startsWith(query.toLowerCase())
      );

Derive the list during render — keep `query` as the single source of truth; a `selected` flag collapses the list after a pick.

What it has to do

  • The fruit list is exactly: Apple, Apricot, Banana, Blueberry, Cherry, Coconut, Date, Grape.
  • There is exactly one `<input>` on the page.
  • Each suggestion renders as an `<li>` containing the fruit's full name as its text.
  • An empty input shows no suggestions.
  • Typing text that matches nothing shows no suggestions (not an error, not stale results).
  • Clicking a suggestion sets the input's value to that suggestion and closes the list — no `<li>` elements remain.

Notes

  • A real autocomplete would debounce the filter when the dataset is large or remote — here it's an in-memory array of 8, so filtering per keystroke is free.
  • Accessibility: the input should be a `role="combobox"` wired to the listbox via `aria-controls` / `aria-activedescendant`; the test harness only checks DOM structure, but production code needs it.
  • Match-strategy matters — `startsWith` is intentional. `includes` would surface noise ("Apple" matching "ple"), and fuzzy ranking is a separate problem.
Asked atMetaGoogleAmazonUberStripeAirbnb

Stuck? The react course covers the ideas this problem is built on.

import { useState } from "react";

const FRUITS = ["Apple", "Apricot", "Banana", "Blueberry", "Cherry", "Coconut", "Date", "Grape"];

export default function App() {
  const [query, setQuery] = useState("");

  // TODO: compute the fruit whose name starts with `query`, case-insensitive.
  // An empty query should produce no suggestions.
  const suggestions = [];

  return (
    <div style={{ fontFamily: "sans-serif", padding: 16, maxWidth: 320 }}>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search fruit..."
        style={{ width: "100%", padding: 8, fontSize: 14, boxSizing: "border-box" }}
      />
      <ul style={{ listStyle: "none", padding: 0, marginTop: 4 }}>
        {suggestions.map((fruit) => (
          <li
            key={fruit}
            // TODO: clicking a suggestion should fill the input with it and
            // close the list.
            style={{ padding: 8, cursor: "pointer", borderBottom: "1px solid #eee" }}
          >
            {fruit}
          </li>
        ))}
      </ul>
    </div>
  );
}

5 tests · +200 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.