Design a News Feed
Infinite scroll done right: pagination strategies, optimistic updates, feed virtualization, image loading, and keeping a long-lived feed fast and accessible.
Published · by Frontend Masters India
A news feed is the classic "design Facebook/Twitter" frontend prompt. The trap is treating it as "a list with infinite scroll." The real difficulty is keeping a feed that grows to thousands of items fast, correct, and accessible over a long session.
1. Clarify scope
- What's in a post? Text, images, video, reactions, comments? Each adds loading and layout concerns.
- How fresh must it be? Real-time push, pull-to-refresh, or load-on-navigate?
- Can users act on posts? Likes and comments mean optimistic updates and conflict handling.
2. Pagination: cursor, not offset
Offset pagination (?page=2) breaks on a feed because new items shift everything down, so you get duplicates and skips. Use cursor-based pagination: the server returns a cursor pointing at the last item, and you ask for "items after this cursor."
GET /api/feed?limit=10
→ { items: [...], nextCursor: "abc123" }
GET /api/feed?limit=10&cursor=abc1233. Infinite scroll with IntersectionObserver
Watch a sentinel element near the bottom of the list; when it enters the viewport, fetch the next page.
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting && !loading) loadMore();
});
observer.observe(sentinelRef.current);Always pair infinite scroll with a "Load more" fallback for keyboard users and offer a path to the footer. Pure infinite scroll is an accessibility and SEO trap.
4. The feed will get slow, so virtualize it
After scrolling a while, thousands of DOM nodes will tank performance. Windowing (react-window, TanStack Virtual) renders only the visible rows plus a small buffer. The catch: feed items have variable heights (an image post vs. a one-liner), so you need dynamic measurement, not fixed row heights.
5. Layout shift and images
Images that load after layout cause jarring cumulative layout shift. Reserve space with known aspect ratios (aspect-ratio or width/height attributes), lazy-load off-screen images (loading="lazy"), and show a low-quality or blurred placeholder first.
6. Optimistic updates for likes
When a user likes a post, update the UI immediately and reconcile with the server in the background. On failure, roll back and surface a quiet error.
setLiked(true); // optimistic
try {
await api.like(postId);
} catch {
setLiked(false); // rollback
toast("Couldn't save your like");
}7. Freshness without losing the user's place
New posts arriving shouldn't yank the scroll position. The standard pattern: buffer them and show a "5 new posts" pill at the top that the user taps to insert them. Never reflow under their thumb.
8. What the interviewer will push on
- "How do you avoid duplicate posts across pages?" Cursor pagination + de-duping by ID on the client.
- "What about a slow network?" Skeleton placeholders, retry with backoff, and cached first page for instant paint.
- "How do you keep memory bounded over a long session?" Virtualization plus optionally dropping far-off-screen data from state.
The one-paragraph summary
A production feed uses cursor pagination, loads more via IntersectionObserver (with a keyboard-accessible fallback), virtualizes variable-height rows to stay fast, reserves space for media to avoid layout shift, applies optimistic updates with rollback for actions, and buffers new posts behind a pill so the user never loses their place.
Before you leave — how confident are you with this?
Your honest rating shapes when you'll see this again. No grades, no shame.
Comments
Loading comments…