Frontend System Design in Action: Implementing A Scalable Feed List
In the last issue, I walked through how I apply the CCDAO framework to implement a Typeahead Search Box, which is often found in many frontend system design interviews.
This time, we’re applying the same method to a different question.
Feed UIs — like what you see in Twitter, LinkedIn, or YouTube — seem simple at a glance. A list of posts, loaded as the user scrolls. But once you account for infinite loading, interaction, real-time updates, and performance, things get a lot more complex.
That’s why feed list design often shows up in frontend system design interviews — it touches nearly every layer of frontend architecture.
In this issue, I’ll walk through how I apply CCDAO to design a scalable, interactive feed UI — one that feels fast, works across devices, and doesn’t fall apart under edge cases.
🔍 Challenge: Design a Social Feed
The interview prompt usually goes like this:
“Design a scalable feed UI, like the one you see in Twitter or LinkedIn.”
At first glance, your mind goes straight to a scrollable list of cards. But then the questions start piling up:
How do we fetch the data — paginated, infinite items, and in real-time?
How do we keep it fast as the list grows?
What happens when the network is slow or fails?
How do we handle likes, comments, and other actions?
How do we avoid rerendering components when one post updates?
And I’ll show you how I would do it with the CCDAO framework, which I use to organise my answers in interviews or just guide me through the designing process for a system.
🧱 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 Modeling
A → API Design
O → Optimization Strategies
Each layer helps me surface the right constraints, design trade-offs, and performance considerations — without getting lost in code.
Let’s apply it to the Feed List.
1. Collect Information
Before designing anything, I would double-check if I understand the domain, the focus that the interviewer is looking at, and also learn more non-functional requirements for the design.
I start with some questions, some of which are common and applicable to most other system design questions, like scaling, devices to support, and so on.
How many feed items are shown per page?
Is this feed static, or does it support real-time updates?
Are there special item types — ads, pinned posts, announcements?
What are the product expectations around ordering, grouping, and refresh behaviour?
Are we optimising for desktop, mobile, or both?
Are there accessibility or localisation needs?
These aren’t just technical details — they define how the entire system behaves.
A feed that loads 20 static items is completely different from one that streams posts in real time, reorders them on likes, or mixes ads between user content.
In interviews, this is where I ask clarifying questions. In real projects, this is where I push for product clarity.
2. Component Structure
Once I understand the feature scope, I sketch the UI as components. This, again, is what most devs feel comfortable with, so don’t miss your opportunities here to demonstrate your experience. The key point here is that you should think not only of the happy path, but also of the other supportive cases, like loading skeleton, offline fallback UI, and the error message UI as well.
Typical structure:
FeedList
– the main container that handles fetching and scroll behaviourFeedItem
– the visual representation of a postLoadTrigger
– a small div that appears near the bottom to trigger the next-page fetchFeedSkeleton
– a skeleton loader while fetchingErrorFallback
– in case of fetch failure(Optional)
StickyHeader
– for filters, date labels, or sorting
Things I consider at this stage:
Should
FeedList
own the data, or delegate it to a custom hook?Do we memoise
FeedItem
to avoid re-renders?Is
LoadTrigger
Implemented viaIntersectionObserver
scroll events?Should interaction state (like toggling a comment thread) be local or lifted?
I’m not aiming for premature generalisation — just clarity and separation of concerns. I want the scroll logic to live in one place, and the post logic to be isolated. Normally, you will have some hints from the interviewer about some UI sketches, so use them properly to make the component hierarchy first, and then add a few more UIs for these unhappy paths.
3. Data Modelling
This layer is crucial, especially for features like real-time updates and optimistic UI.
I often start with something like:
type Post = {
id: string;
author: User;
content: string;
timestamp: string;
type: 'standard' | 'ad' | 'announcement';
};
But as the design progresses, this evolves. I might introduce:
Normalised state, where posts are stored in an
entities
map by IDA
pageInfo
object for the pagination stateA
source
field to track where a post came from (user, server push, preload)
Normalised shape:
type FeedState = {
ids: string[];
entities: Record<string, Post>;
pageInfo: {
nextCursor?: string;
hasMore: boolean;
};
}
This way, if a post gets updated (say, a new like), I can update just that post’s data — no need to refetch or rerender the whole list. I have made a video on the normalisation as well if you prefer videos:
This structure also makes preloading, optimistic updates, and live merges much easier.
4. API Design
I’ve seen a lot of feeds break down because the API doesn’t align with how the UI needs to function.
For this kind of UI, I prefer cursor-based pagination over offset:
GET /feed?cursor=abc
→ {
items: [...],
nextCursor: "def"
}
This avoids duplication when posts are added/removed and enables infinite scroll without issues.
If real-time updates are required, I’d plan for:
WebSocket/SSE support for server-pushed updates
A way to merge live items into the current list, with scroll state preserved
Filtering logic to avoid pushing duplicates or ads out of place
Other things I surface in interviews or designs:
Do we need to retry with exponential backoff?
Should we cancel in-flight fetches if the user scrolls away?
How do we preload future pages for a smoother experience?
5. Optimisation Strategies
This is where the design gets stress-tested. A feed UI that feels fine with 10 posts may crumble with 500 — or become janky on low-end mobile.
Surely it’s a big topic here and there is no single correct way to frame your answers, but what I focus on for a feed list are:
Virtualization
I use libraries like react-virtual
to render only visible items. Without this, long feeds destroy performance. You can play with the library example, but I would suggest spending some time writing one yourself before the interview.
Prefetching
I trigger the next page fetch before the user hits the bottom, using an invisible <LoadTrigger />
+ IntersectionObserver
.
Loading & Error States
I prefer skeleton loaders over spinners — they feel more responsive. For network errors, I show retry options and log telemetry if needed.
Optimistic Updates
I apply an optimistic UI for interactions like likes. I’ll summarise my notes recently and will share them in a separate issue.
For new posts from the server, I either insert them at the top or show a "new posts available" button, without breaking the user's scroll.
Ending
Feeds are one of the best examples of frontend system design in the wild.
They combine async data, pagination, user interaction, rendering performance, and real-time behaviour — all in one scrollable box.
In the next issue, I’ll dive deep into some detailed explanations of frontend system design concepts like normalisation, virtualisation, optimistic updates and so on. Please also don’t hesitate to let me know what topics you’re interested in.
Until then, stay pragmatic.