Frontend System Design in Action: Implementing Typeahead
In this issue, I want to show how I would apply the CCDAO framework in a frontend system design interview.
A few months ago, I introduced the framework and explored each layer in depth. This time — and in the next issue — I want to walk through how it works in practice.
Let’s start with a relatively common interview question: Design a typeahead search box.
It sounds manageable. You picture an input, a list of suggestions, maybe a quick API call.
But after a minute of thinking, questions start to surface.
What if the results are personalised?
What if the user is offline?
How should you handle loading states, debouncing, and keyboard navigation?
Simple features like this often hide more complexity than they show.
In moments like that, I rely on a framework — not because it gives me the answers, but because it helps me organise my thinking.
The CCDAO Framework
I’ve discussed the CCDAO framework before https://juntao.substack.com/p/navigating-frontend-system-design and https://juntao.substack.com/p/deeper-dive-into-the-ccdao-framework, but if you’re new: it’s a structure I use to reason through frontend system design problems.
C → Collect Information
C → Component Structure
D → Data Modelling
A → API Design
O → Optimisation Strategies
Each layer leads naturally into the next. I don’t treat it as a checklist — more like a lens I switch between as needed.
You may have seen this sketch in the video version of this issue:
Here’s how I would apply the framework when designing a typeahead search box.
1. Collect Information
Before designing anything, I try to understand what I’m actually solving. That’s just as important as technical knowledge during an interview.
Use the time to gather context. Don’t just focus on the happy path — edge cases, error handling, and non-functional requirements matter just as much (if not more).
Questions I typically ask:
What’s the scale? Are we searching a local list or querying a remote index of millions?
Are the results personalised per user?
What platforms are supported — desktop, mobile, both?
Do we have accessibility or internationalisation requirements?
Can we cache results, or are they fetched fresh each time?
Should we support pagination or infinite scrolling?
What does a result look like — just text, or images, tags, metadata?
Do we need keyboard navigation? Should suggestions appear on focus or only after typing?
These aren’t just implementation details — they influence the design from the ground up.
2. Component Structure
Once I understand the scope, I think about the interface.
This is usually the most familiar part for frontend engineers. You can think visually — sketch what you’ve seen in real-world apps: Google search, Airbnb filters, command palettes.
Most typeahead UIs include:
A
InputField
(controlled)A
SuggestionList
that appears on focus or inputSuggestionItem
s — clickable, keyboard-friendlyA
LoadingIndicator
,EmptyState
, orErrorMessage
component
If the logic grows complex, I might extract a useTypeahead
hook to handle debounce, fetch, state transitions, and list visibility. For simple cases, the inline state is fine.
Things I consider:
What triggers the suggestion list? Focus, input, debounce?
When does the list close — on blur, selection, escape key?
How do keyboard and mouse interactions work together?
A clear structure keeps the component flexible, not rigid.
3. Data Modelling
This layer is less about deep backend modelling and more about shaping the data to suit the UI. If time allows, I might also touch on state management strategies here.
I usually start with something simple:
type Suggestion = {
id: string;
label: string;
url: string;
}
Then expand as needed:
Grouped results (e.g., trending, recent, personalised)
Extra fields like icons, categories, and tags
A
source
ortype
field for merging local and remote resultsPagination metadata
Normalised shape to support caching and consistent updates
Example:
type Suggestion = {
id: string;
label: string;
url: string;
}
type FullSuggestions = {
trending: Suggestion[];
recent: Suggestion[];
personalised: Suggestion[];
}
type PaginatedSuggestions = {
pageInfo: PageInfo;
items: Suggestion[];
}
The goal isn’t to make it perfect upfront, but skipping this step usually creates problems later.
4. API Design
Eventually, the input needs to talk to something external.
You don’t need to go deep into API versioning, authentication, or cache headers in an interview, but you should show awareness of the data flow.
In many cases, the API might look like:
GET /api/search?q=react&pageSize=8&page=2
For larger systems or teams using GraphQL:
query Search($q: String!) {
search(query: $q) {
id
label
url
image
type
}
}
With pagination:
query Search($q: String!) {
search(query: $q) {
trending {
...Suggestion_Fragment
}
recent {
...Suggestion_Fragment
}
result {
edges {
node {
...Suggestion_Fragment
}
}
}
}
}
Things I try to surface:
Should we debounce input before firing requests?
Do we cancel in-flight queries when the input changes?
Do we retry failed requests, or surface errors directly?
Can we preload something useful before the user types — e.g., trending or recent terms?
This part often reveals product expectations and integration constraints I hadn’t considered earlier.
5. Optimisation Strategies
Once the core logic works, I shift focus to how the feature behaves in real use. This is where polish shows — and where many designs fall short.
It’s also where you reflect on earlier decisions. Are they holding up? Would you structure things differently to optimise better?
Make it responsive
Debounce input to reduce noise
Cancel stale requests to avoid flickers and race conditions
Show skeletons, loading states, or spinners
Provide clear empty and error states
Make it predictable
Cache recent queries (in memory or localStorage)
Normalise data to prevent unnecessary rerenders
Avoid duplicate fetches for the same input
Make it inclusive
Support keyboard interaction (arrows, enter, escape)
Add proper ARIA roles for screen readers
Handle right-to-left layouts and localised content
Fallback gracefully when offline
You won’t need all of these in every situation. But being able to talk about them — and justify the ones you choose — shows you’ve thought beyond the happy path.
If you prefer, I have created a video on my YouTube channel here for you.
Ending
System design interviews aren’t about having the “right” answer. They’re about how you approach ambiguity. How do you uncover what matters? How do you make trade-offs? The CCDAO framework helps me do that.
Not by providing a template, but by giving me just enough structure to stay grounded while navigating open-ended problems. I’ve used it to design modals, pagination, filtering panels, infinite scrolling, and in this case, a typeahead search box.
Try to apply the method with something you’re interested in, or you want to practice for your next system design interview, and I’m keen to know how it goes.