Frontend system design: design a News Feed (Facebook)
Infinite-scroll feed with cursor pagination; virtualized list for large scrolls; per-post components with mixed media; optimistic likes/comments; ranking served by backend; client caches via React Query keyed on cursor; image/video lazy-load; offline / poor-network resilience; accessibility for the post structure and live updates.
A news feed at scale is the canonical frontend system-design question. The architecture is infinite scroll over server-ranked, cursor-paginated posts, with optimistic interactions and aggressive media optimization.
1. Pagination — cursor, not offset
Offset pagination breaks when items are inserted at the top of a feed (you'd see duplicates / skips). Cursor pagination (server returns an opaque cursor with each page) is correct:
GET /feed?cursor=eyJ...
→ { items: [...], nextCursor: "eyJ..." }2. Infinite scroll — IntersectionObserver
Sentinel at the bottom of the rendered list:
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ["feed"],
queryFn: ({ pageParam }) => fetchFeed(pageParam),
getNextPageParam: (last) => last.nextCursor,
});
const ref = useRef();
useEffect(() => {
const obs = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasNextPage) fetchNextPage();
});
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, [hasNextPage]);3. Virtualization
Without virtualization, 10k rendered posts kill memory and scroll perf. react-virtual / react-window for fixed-ish heights; for variable heights, measure and cache.
4. Post component
<Post>
├ <Header (avatar, name, time, menu) />
├ <Body (text, embeds) />
├ <Media (image/video/link preview) />
├ <Actions (like, comment, share) />
└ <Comments (preview, expandable) />Memoize so liking one post doesn't re-render the feed.
5. Interactions — optimistic
Like, react, comment — all optimistic with rollback on failure. Idempotency keys for retry safety.
6. Media
- Lazy load below the fold (IntersectionObserver or
loading="lazy"). - Responsive images with
srcset; modern formats (WebP/AVIF). - Video — autoplay only when in view, muted, controls on tap.
- Aspect-ratio reserved to prevent CLS.
7. Ranking
Backend concern (engagement model, recency, social graph). Frontend's job is to render the order it receives and not re-sort.
8. Caching
- React Query keyed on cursor — back-navigation restores the scroll position with a warm cache.
- Persist the first page to localStorage for an instant offline-y open.
- Invalidate selectively on post-create.
9. Real-time updates
- New posts above — a "Show N new posts" pill (don't auto-shift the scroll).
- New comments/likes — push via WebSocket or poll counts periodically.
10. Performance & resilience
- Code-split heavy components (video player, reactions picker).
- Skeleton placeholders matching post layout.
- Retry with backoff on transient errors.
- Service worker for offline shell + cached feed.
11. Accessibility
- Each post is an
<article>with an accessible name. - Image alt text; video captions.
- Keyboard: J/K to next/prev post (FB does this), focus management on expanding comments.
- Live region for "new post" announcements (polite).
Interview framing
"Feed scrolls infinitely with cursor pagination — offset breaks as new posts arrive at the top. IntersectionObserver triggers fetchNextPage; React Query keyed on cursor caches pages so back-nav restores state. Virtualize the list once it's long. Each post is a memoized component; likes and comments are optimistic with idempotency keys. Media is the heaviest cost: lazy-load, responsive srcset, modern formats, reserved aspect-ratio. New top-of-feed posts surface as a 'Show N new' pill rather than auto-shifting. Ranking is the server's problem; the client renders the order received."
Follow-up questions
- •Why cursor pagination instead of offset?
- •How do you handle new posts arriving while the user is scrolled?
- •Virtualization with variable-height posts — how?
- •How would you make the feed resilient to network failure?
Common mistakes
- •Offset pagination — duplicates/skips as content shifts.
- •Rendering everything; no virtualization.
- •Auto-jumping the scroll on new posts.
- •Not memoizing posts — like one, re-render thousand.
- •No aspect-ratio on media — CLS.
Performance considerations
- •Virtualize + memoize posts. Lazy-load + decode-async images. Code-split heavy interactions. Throttle scroll handlers via rAF or IntersectionObserver (not scroll-event polling).
Edge cases
- •Very tall posts (long text, multiple images).
- •Network drops mid-scroll.
- •Switching feed (Home → Profile) — separate cache keys.
- •Rapid scroll past unloaded items.
Real-world examples
- •Facebook News Feed, Twitter/X timeline, Instagram feed, Reddit.