Design a News Feed
Frontend system design for a social news feed — ranking surface, virtualization, caching, realtime, and tradeoffs.
- system-design
- feed
- performance
- caching
- interview
Scope the problem
Frontend system design interviews die when candidates design ranking ML and Cassandra clusters for 40 minutes. Lock scope early.
In scope (frontend):
- Home feed (following / For You — pick one primary)
- Post card: author, text, media, social counts, actions
- Composer entry (not full rich-text editor deep dive unless asked)
- Pagination / infinite scroll
- Basic realtime (new posts indicator)
- Performance, caching, a11y, empty/error states
Out of scope unless asked:
- Fan-out-on-write vs fan-out-on-read internals
- Ranking model training
- CDN POP topology
- Ads auction
Scale assumptions (state out loud):
- Mobile-heavy; variable network
- Media-heavy posts; long sessions
- Personalized feed; tens of posts per viewport session
- Read-heavy with bursty writes (likes/comments)
Requirements & metrics
| Type | Examples |
|---|---|
| Functional | View feed, like, open comments, create text post |
| Non-functional | TTI/LCP for first posts, scroll smoothness (INP), offline soft-fail |
| Product | Time to first meaningful post, scroll depth, error rate |
Clarify: chronological vs ranked; logged-in only; multi-media support.
High-level UI architecture
┌──────────────────────────────────────────────────────────┐
│ App shell (auth, nav, feature flags, toasts) │
├───────────────┬────────────────────┬─────────────────────┤
│ Feed route │ Composer modal │ Post detail / │
│ list + cache │ (lazy) │ comments route │
├───────────────┴────────────────────┴─────────────────────┤
│ Design system: Card, Media, Avatar, ActionBar │
└──────────────────────────────────────────────────────────┘
Route-split composer and comments so the feed bundle stays lean.
Component tree
FeedPage
├── FeedHeader / filters (Following | For You)
├── ComposerEntry
├── NewPostsPill // realtime “N new posts”
├── VirtualizedFeedList
│ └── PostCard
│ ├── PostHeader
│ ├── PostBody (text clamp + expand)
│ ├── PostMedia (image/video)
│ └── PostActions (like, comment, share)
└── FeedSentinel / Load more
Data model (client)
type PostId = string;
type Post = {
id: PostId;
author: { id: string; name: string; avatarUrl: string };
createdAt: string; // ISO
text: string;
media?: { type: "image" | "video"; url: string; width: number; height: number }[];
stats: { likes: number; comments: number; shares: number };
viewer: { liked: boolean };
};
type FeedPage = {
items: Post[];
nextCursor: string | null;
};
API sketch:
GET /feed?cursor=&tab=→FeedPagePOST /posts/:id/like→{ liked, likes }GET /posts/:id/comments?cursor=- Optional: WebSocket / SSE
feed:new_post
Prefer cursor pagination over offset (stable under inserts).
Rendering & list performance
Critical path
- Shell + auth
- First feed page (SSR/stream if product allows; else client fetch with skeletons)
- Images below the fold lazy; LCP image prioritized
Virtualization
Mount ~10–20 cards in the viewport window. Estimate heights or measure; media aspect-ratio boxes reserve space (CLS).
See machine-coding cousin: Infinite Scroll Feed.
Media
- Responsive images (
srcset), blurhash/LQIP placeholders - Video: poster + click-to-play (autoplay muted only if product requires; respect data saver)
- Fixed aspect boxes from server width/height
State & caching
| Layer | What | Strategy |
|---|---|---|
| Memory (React Query / SWR) | Feed pages by tab + cursor |
Infinite query; stale-while-revalidate |
| Normalized store | posts by id |
Likes update one entity, all views agree |
| HTTP cache | Avatars, static assets | Long cache + hash |
| Service Worker | App shell | Network-first for feed API |
| Local | Draft composer | localStorage / IndexedDB |
Like interaction: optimistic toggle → rollback on failure. Dedupe in-flight like requests.
New posts while scrolling: don’t jump the list; show a pill “View N new posts” that prepends or refreshes from top.
Realtime
| Approach | When |
|---|---|
| Polling every 30–60s | Simple; fine for many products |
| SSE | Server push, one-way |
| WebSocket | Bidirectional; chat-adjacent products |
Frontend concern: reconnect backoff, auth refresh, avoid stampeding re-fetch of full feed on every event (send ids, hydrate).
Accessibility
- Feed as list of articles; action buttons named (“Like post by Ada”)
- Keyboard operable actions
- Infinite scroll + Load more fallback
- Live region for “N new posts” (polite)
- Reduced motion on media carousels
Performance budgets (example)
- Feed route JS: < 150–200KB gzip initial
- First posts painted with skeletons ≤ 1–2s on mid-tier 4G target
- Scroll: avoid layout thrash in action bars; use CSS for like micro-interactions
Techniques: code-split comments, defer analytics, content-visibility for offscreen cards, virtualize early if demoing scale.
Tradeoffs to discuss
- SSR feed vs SPA — personalization vs first paint / cache
- Offset vs cursor — cursor wins for live feeds
- Optimistic likes vs wait for server — UX vs integrity
- Virtualization complexity vs max list length
- Realtime density — battery vs freshness
Closing structure
End with: scoped requirements → architecture → data/API → critical path → cache → realtime → a11y/perf → tradeoffs → monitoring (RUM: LCP feed, error rate, action latency).
Related
- Design YouTube (Frontend)
- Design Autocomplete Typeahead
- Infinite Scroll Feed
- Core Web Vitals
- Meta Frontend Interview