Design a Debounced Product Search Box
AmazonFrontend Engineer · OnsiteLast updated
Design and implement an accessible, resilient product search-as-you-type component featuring debounced requests, race-condition handling via request cancellation, client-side caching, and keyboard navigation.
Problem Statement
Design and implement a product search dropdown component (such as an Amazon-style auto-suggest search bar). The component must fetch suggestions from a mock backend API as the user types, render up to 10 ranked results, and maintain high responsiveness under variable network latency.
Requirements
1. Functional Requirements
- Debounced Fetch: Trigger network requests only after the user stops typing for
300ms. - Stale Request Handling: Guard against race conditions where older requests resolve after newer ones. Cancel pending in-flight requests or ignore stale responses.
- Client-Side Cache: Cache recent query results in memory to avoid duplicate network calls when users backspace or repeat queries.
- Keyboard Navigation: Support
ArrowDownandArrowUpto navigate suggestions,Enterto select, andEscapeto close the dropdown. - Accessibility (a11y): Implement ARIA attributes adhering to the WAI-ARIA
comboboxpattern (role="combobox",aria-autocomplete="list",aria-expanded, andaria-activedescendant). - States: Gracefully handle loading, empty results, and network error states.
2. Component Interface
You may implement this in React (hook or component) or modern TypeScript/vanilla JavaScript.
interface Suggestion {
id: string;
title: string;
category: string;
}
interface SearchBoxProps {
fetchSuggestions: (query: string, signal: AbortSignal) => Promise;
onSelect: (item: Suggestion) => void;
debounceDelayMs?: number; // default: 300
maxResults?: number; // default: 10
}
Example Workflow
- User types "shoe":
- A timer starts for 300ms.
- If the user types "s" then "shoes" within 200ms, the previous timer clears.
- At 300ms after "shoes", an HTTP request is dispatched with an
AbortSignal. - Dropdown renders up to 10 suggestions with an active loading indicator during fetch.
- User presses
ArrowDown:- Focus visually highlights the first suggestion (
index = 0). aria-activedescendantis updated to point to the active item's DOM id.
- Focus visually highlights the first suggestion (
- User presses
Enter:onSelect(item)is triggered, the dropdown closes, and the input reflects the selection.
Edge Cases
- Empty or whitespace-only inputs should clear suggestions and not trigger network requests.
- Rapid query alterations (e.g., typing "apple", backspacing to "app", re-typing "apple") should retrieve the result from the local cache immediately without making an API call.
- Handle network failures gracefully without clearing the user's current input text.
Hints
Hint 1
Use an AbortController instance stored across renders to cancel previous pending fetch requests whenever a new request is triggered.
Hint 2
Implement an in-memory Map or LRU cache where the normalized search query string acts as the key to store past results.
Hint 3
Use aria-activedescendant on the input pointing to the id of the currently highlighted list item rather than shifting DOM focus away from the input.
Follow-up questions
- How would you handle analytics logging for search impressions vs. debounced keystrokes to avoid overwhelming telemetry services?
- How would you support bolding or highlighting matching sub-strings inside the suggestion titles without introducing XSS vulnerabilities?
- If the dropdown needs to support categorized groups (e.g., 'Departments', 'Brands', 'Products') with different layouts, how would you refactor your component's state and rendering logic?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.