. Design a News Feed (Infinite Scroll & Real-time) – Frontend System Design Interview Guide
Design a news feed experience - an infinite scrolling feed with images, videos, likes, comments, and real-time updates. Handle millions of active users with optimal performance.
Backend as Black Box: Assume you have APIs for posts, likes, comments, and real-time updates. Focus on the frontend architecture.
Key Challenges
- Media-heavy infinite feed with smooth 60fps scrolling
- Cursor pagination stability under live inserts/deletes
- Optimistic like/comment interactions with safe rollback
- Realtime updates without disrupting scroll position
- Mobile-first performance on degraded networks
RADIO Framework
Use the RADIO framework to structure your solution:
- Requirements: Clarify functional and non-functional requirements
- Architecture: High-level component structure and system design
- Data Model: Data structures, API contracts, state management models, and data flow
- Interface: Key implementation details, component interfaces, and integration points
- Optimization: Performance and scalability improvements
Note: RADIO framework helps structure your system design approach. Focus on high-level design and architecture decisions. Avoid diving into low-level implementation details unless specifically asked.
Interview Approach
System design interviews are collaborative discussions where you work with the interviewer to explore different approaches and trade-offs. There are no single "correct" answers—instead, focus on:
- Asking clarifying questions to understand requirements and constraints
- Discussing trade-offs for different architectural approaches
- Starting with high-level design before diving into implementation details
- Considering edge cases and how to handle them gracefully
- Thinking about scalability and performance from the start
Important: Revise your 2-minute TL;DR before going to system design interviews, then expand through RADIO during the discussion. In real interviews, you won't have enough time to cover every trade-off in depth. Prioritize explicit comparisons (cursor vs offset, polling vs SSE vs WebSocket, CSR vs SSR/ISR), and browse the Web Fundamentals for deeper reference.
For more guidance on approaching system design interviews, see the introduction section in the sidebar.
When users scroll through their feed, they expect smooth performance, instant interactions, and seamless media loading—even with thousands of posts. This solution designs a news feed that handles infinite scrolling, real-time updates, optimistic interactions, and media optimization while maintaining 60fps performance. The key insight: a good feed feels instant, preserves scroll position, and gracefully handles network issues.
HLD interview focus: Requirements, architecture, tradeoffs, data flow, and scaling decisions. Low-level implementation snippets are collected in the final optional section and are only needed when explicitly asked.
I'll start by defining what makes a great feed experience—what questions does a user need answered as they scroll? Then I'll design the data pipeline that handles infinite scrolling, real-time updates, and optimistic interactions. Finally, I'll design the component architecture with clear separation between server state and UI state.
Why this approach?
Most candidates build a "list of posts." Strong candidates build a "feed experience"—a system that feels instant, preserves scroll position, and handles real-time updates gracefully. The difference is thinking about the entire user journey, not just rendering a list.
Think of this like building a social media feed. You don't just show posts—you handle infinite scrolling, real-time updates, optimistic interactions, and media loading. Same principles apply here.
Requirements Exploration Questions
DiscoveryAsk your interviewer these questions to refine requirements
What type of content should the feed support?
- Images only vs images + videos
- Image formats (JPEG, WebP, AVIF)
- Video formats (MP4, WebM)
- Maximum file sizes
What devices and network conditions?
- Mobile (3G, 4G, 5G) vs desktop (WiFi)
- Offline support requirements
- Progressive loading expectations
What interactions should be supported?
- Like, comment, share, save, report
- Double-tap to like
- Long-press actions
- Swipe gestures (mobile)
What is the expected feed size?
- Average posts per user
- Maximum posts to keep in memory
- When to unload posts (performance)
What real-time features are needed?
- Live like counts
- New post notifications
- Story updates
- Live video streams
Functional Requirements
Must HaveMVP (Core Features - What I'd Design First):
- Infinite scrolling feed (10–20 posts per page)
- Images with progressive loading (videos optional)
- Like/comment interactions with optimistic updates
- Skeleton loading + error states
- Cursor pagination
- Basic caching + retry logic
- Accessibility basics (semantic HTML, ARIA labels)
Advanced Features (Add If Time Permits):
- Real-time updates (WS/SSE) with "new posts" banner
- Offline queue + IndexedDB
- Video autoplay policies + data saver mode
Non-Functional Requirements
Quality BarPerformance Targets:
- Initial load: < 3s (Time to Interactive)
- Scroll performance: Maintain 60fps
- Interaction feedback: < 100ms (optimistic updates)
- Memory usage: < 200MB for 100 posts
Scalability Goals:
- Support 1M+ concurrent users
- Handle 10K+ posts in feed
- Efficient memory management (virtual scrolling)
Reliability & UX:
- Graceful error handling with retries
- Skeleton loading and clear empty/error states
- New-post banner without interrupting scroll position
Accessibility & Security:
- WCAG 2.1 AA baseline (keyboard + screen reader support)
- Proper semantic HTML and ARIA labels
- XSS/CSRF protection and strict API auth
Network Resilience:
- Works on slow connections (3G/4G)
- Compressed responses (gzip/brotli)
- CDN-backed media delivery
Observability:
- Error tracking and monitoring
- Core Web Vitals tracking (LCP, FID, CLS)
- API latency/error dashboards
What we're doing: Designing the React component structure, data flow, and how components communicate. This is where we decide the component hierarchy, props flow, and React-specific patterns.
React Component Architecture
What we're building: A React application with clear component hierarchy, efficient data flow, and separation of concerns between UI, state, and data fetching.
Component Hierarchy
Top-Level: FeedContainer
- Root component managing feed state and pagination
- Handles infinite scroll logic and prefetching
- Coordinates real-time updates from WebSocket
- Manages virtual scrolling for performance
Component Tree:
FeedContainer (smart component)
├── FeedHeader
│ ├── PullToRefresh
│ └── NewPostsBanner
├── VirtualizedFeedList
│ └── PostCard[] (presentational)
│ ├── PostHeader
│ │ ├── Avatar
│ │ ├── Username
│ │ └── Timestamp
│ ├── MediaViewer
│ │ ├── ImageCarousel
│ │ └── VideoPlayer
│ ├── PostContent
│ │ ├── Caption (with Mentions/Hashtags)
│ │ └── LocationTag
│ ├── InteractionBar
│ │ ├── LikeButton
│ │ ├── CommentButton
│ │ └── ShareButton
│ └── CommentSection
│ ├── CommentList
│ └── CommentInput
└── LoadingState
└── SkeletonCard[]React-Specific Architecture Patterns
StructuredContainer/Presentational Pattern:
- FeedContainer: Smart component (logic, state, data fetching)
- PostCard: Presentational component (receives props, renders UI)
- Clear separation: Container handles data, Presentational handles display
FeedContainer:
- Uses React Query for feed data fetching
- Manages scroll position and pagination state
- Handles WebSocket connection lifecycle
- Coordinates optimistic updates
PostCard:
- Receives post data via props
- Handles local UI state (expanded comments, image loaded)
- Triggers callbacks for interactions (onLike, onComment)
- Memoized to prevent unnecessary re-renders
MediaViewer:
- Uses Intersection Observer for lazy loading
- Handles progressive image loading
- Manages carousel state (current image index)
- Responsive image sizing based on viewport
Data Flow in React
Top-Down Data Flow (Props):
FeedContainer (fetches posts via React Query)
↓ props: posts[]
PostCard (receives post data)
↓ props: post, onLike, onComment
InteractionBar (receives handlers)Bottom-Up Events (Callbacks):
LikeButton (onClick)
↓ callback
PostCard (handleLike)
↓ callback
FeedContainer (updatePostState)
↓ mutation
React Query (optimistic update + API call)Real-time Updates (WebSocket → React Query):
WebSocket (receives update)
↓ event handler
FeedContainer (processUpdate)
↓ update cache
React Query (setQueryData)
↓ automatic re-render
PostCard (receives updated props)Tradeoffs & Comparisons
- CSR vs SSR/ISR: Rendering Strategies
React Architecture Diagram
Frontend architecture showing React component structure and data flow:
┌─────────────────────────────────────────────────────────────────────────────┐
│ NEWS FEED ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ UI LAYER │ │
│ │ ┌────────────────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ Post Feed Consumer │ │ Post Creation │ │ │
│ │ │ FeedContainer Component │ │ CreatePost Component │ │ │
│ │ │ Infinite scroll + Posts │ │ Form + Media upload │ │ │
│ │ └────────────────────────────┘ └────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ DATA & STATE MANAGEMENT │ │
│ │ ┌────────────────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ React Query │ │ Zustand / Context │ │ │
│ │ │ Server state + Cache │ │ Client state + Persist │ │ │
│ │ └────────────────────────────┘ └────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
└──────────────────────────────────────┼───────────────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ REST API + WebSocket │
│ (Backend as black box) │
└──────────────────────────────┘Why React Query + Zustand?
- React Query for server state (posts, comments) - handles caching, background refresh
- Zustand for client state (UI preferences, scroll position) - simple, no re-render issues
- Clear separation - server data vs UI state never mixed
Data Flow Diagrams
Key user flows through the system:
Initial Feed Load:
User opens app
│
▼
┌─────────────────────────┐
│ 1. Check cache │
│ (React Query) │
└───────────┬─────────────┘
│ Cache miss
▼
┌─────────────────────────┐
│ 2. Show skeleton UI │ ← Immediate feedback
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 3. Fetch GET /feed │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 4. Store + render posts │
└─────────────────────────┘Infinite Scroll (Prefetch at 80%):
User scrolls to 80%
│
▼
┌─────────────────────────┐
│ 1. Prefetch next page │ ← Before user needs it
│ (background) │
└───────────┬─────────────┘
│
User reaches bottom
│
▼
┌─────────────────────────┐
│ 2. Data already cached │ ← Instant rendering
│ Render immediately │
└─────────────────────────┘Optimistic Like:
User double-taps
│
├──▶ Update UI immediately (optimistic)
│
└──▶ POST /like (background)
│
┌────┴────┐
▼ ▼
Success Failure
│ │
▼ ▼
Confirm RollbackWhy Prefetch at 80%?
- Seamless experience - Data ready before user needs it
- No loading spinners - Next page renders instantly
- Network efficient - Only one prefetch, not wasted requests
Entity and interface contract shape with cache/reconciliation model for a frontend system design interview (backend treated as a black box).
Entity and interface contract shape for a frontend system design interview (backend treated as a black box).
1) Component prop interfaces (boundaries)
Define clean boundaries across feed container, post card, and interaction controls.
FeedContainerProps: feed context and list-level callbacksPostCardProps: post summary + interaction handlersFeedHeaderProps: refresh and new-post banner controlsCommentDrawerProps: thread UI state and submit callbacks
2) Hook interfaces (consumption contracts)
Use hook return contracts to keep integration discussion concrete but lightweight.
Data model (Entities)
A feed is mostly server-originated data (posts/authors/comments). Client data is mostly UI state + optimistic/offline queues.
| Entity | Source | Belongs to | Key fields |
|---|---|---|---|
| FeedPage | Server | Feed UI | items: PostSummary[], pageInfo |
| PostSummary | Server | Feed UI | subset of Post used for card rendering |
| Post | Server | Post detail | id, authorId, caption, media[], likeCount, commentCount, likedByMe, createdAt, updatedAt |
| Author (User) | Server | Client cache | id, username, avatarUrl |
| Comment | Server | Comment UI | id, postId, authorId, content, createdAt |
| PendingAction | Client | Offline/optimistic queue | type, entityId, payload, idempotencyKey, retryCount, createdAt |
Client cache shape (recommended)
Even if we use React Query, conceptually the cache is:
postsById: Record<PostId, Post>usersById: Record<UserId, Author>feedIds: PostId[]pageInfo: { nextCursor, hasMore }
This enables:
- Deduping across pages
- O(1) realtime patches by
postId - Stable rendering order while data updates
Deep dive: Data Normalization
Consistency & reconciliation rules
- Writes are idempotent via
idempotencyKey. - Realtime events include
eventIdand/orupdatedAt. - Ignore stale realtime events older than cached
updatedAt.
Tradeoffs & Comparisons
- Normalized vs Denormalized: Data Normalization
Component Boundaries
StructuredFeedContainer
Owns pagination state, refresh behavior, and new-post banner lifecycle.
PostCard
Renders post payload and emits interaction callbacks.
InteractionBar
Contains like/comment/share intent handlers and local pending state.
CommentDrawer
Loads and submits comment threads for the selected post.
export interface FeedContainerProps {
userId: string;
onOpenPost: (postId: string) => void;
}
export interface PostCardProps {
post: PostSummary;
onLike: (postId: string, liked: boolean) => void;
onComment: (postId: string) => void;
onShare: (postId: string) => void;
}
export interface FeedHeaderProps {
newPostsCount: number;
onRefresh: () => void;
onShowNewPosts: () => void;
}
export interface CommentDrawerProps {
postId: string;
isOpen: boolean;
onClose: () => void;
}export interface UseFeedResult {
items: PostSummary[];
fetchNext: () => void;
hasMore: boolean;
isLoading: boolean;
}
export interface UseLikeMutationResult {
like: (postId: string, liked: boolean) => Promise<void>;
isPending: boolean;
}
export function useFeed(): UseFeedResult {
throw new Error('Contract-only snippet');
}
export function useLikeMutation(): UseLikeMutationResult {
throw new Error('Contract-only snippet');
}React interfaces & integration patterns (props, hooks, callbacks).
This section covers API contracts and React consumption patterns.
API contracts (Backend as black box)
| API | Type | Purpose |
|---|---|---|
/api/feed | GET | Fetch paginated feed items (cursor-based) |
/api/posts/:id | GET | Fetch full post details (optional hydration) |
/api/posts/:id/like | POST | Idempotent like/unlike (optimistic UI) |
/api/posts/:id/comments | GET/POST | Read comments (paged) + add comment |
/realtime/feed | WS or SSE | Live counters + "new posts available" signal |
Feed
GET /api/feed?cursor=<opaque|null>&limit=10
=> {
items: PostSummary[],
pageInfo: { nextCursor: string | null, hasMore: boolean }
}Contract rules:
- Cursor is opaque (client never parses it)
- Ordering is stable (rank key + deterministic tie-break)
- Client dedupes by
postIdacross pages
Post detail (optional hydration)
GET /api/posts/:id
=> { post: Post }Like (idempotent)
POST /api/posts/:id/like
body: { like: boolean, idempotencyKey: string }
=> { likedByMe: boolean, likeCount: number, updatedAt: string }Comments (paged)
GET /api/posts/:id/comments?cursor=<opaque|null>&limit=20
POST /api/posts/:id/comments
body: { content: string, idempotencyKey: string }
=> { comment: Comment, commentCount: number }Real-time events (schema)
// WS/SSE payloads (examples)
{ type: 'POST_LIKES', eventId: string, postId: string, likeCount: number, updatedAt: string }
{ type: 'POST_COMMENTS', eventId: string, postId: string, commentCount: number, updatedAt: string }
{ type: 'NEW_POST_AVAILABLE', eventId: string, postId: string, createdAt: string }Type definitions used in contracts
interface PostSummary {
id: string;
authorId: string;
caption: string;
likeCount: number;
commentCount: number;
likedByMe: boolean;
createdAt: string;
updatedAt: string;
}
interface Post extends PostSummary {
media: Array<{ id: string; type: 'image' | 'video'; url: string }>;
}
interface Comment {
id: string;
postId: string;
authorId: string;
content: string;
createdAt: string;
}3) Integration patterns (React wiring)
- Data down, events up: hooks own server-state; UI emits user intent.
- Optimistic interactions: apply local like/comment patch, rollback on failure.
- Realtime patching: update counters by
postId; defer inserts behind banner. - Scroll preservation: keep viewport stable during refresh and live updates.
Integration Patterns
StructuredOptimistic patch flow
Update UI immediately, rollback on failure, revalidate on settle.
Banner-first realtime
Patch counters immediately; insert new posts only on banner action.
Stable rendering order
Keep entity ids and list order deterministic under updates.
Scroll-safe refresh
Preserve scroll anchor while prepending or refreshing items.
What we're doing: Optimize rendering, media loading, request behavior, and interaction responsiveness for scale.
Performance Optimizations
StructuredVirtual Scrolling
- Keep DOM window small
- Render visible rows + buffer
- Recycle row containers during scroll
Image Optimization
- Lazy load with Intersection Observer
- Blur-up placeholders
- Responsive srcset/sizes
- CDN transforms and modern formats (WebP/AVIF)
Infinite Scroll Strategy
- Initial page size 10-20
- Prefetch near 80% scroll
- Cancel stale in-flight requests
- Cursor-based pagination only
Network Optimizations
- Route/feature-level code splitting
- Compression (gzip/brotli)
- HTTP/2 multiplexing
- Priority hints and prefetch where useful
Rendering Optimizations
- React.memo for heavy post cards
- Stable callbacks/selectors
- Batched updates
- Debounced scroll observers
Mobile Optimizations
- Larger touch targets
- Data-saver friendly media defaults
- Conservative autoplay behavior
Optimistic UI Updates
Use one predictable optimistic flow: update UI immediately, rollback on failure, then revalidate.
onMutate: snapshot -> optimistic patch
onError: rollback(snapshot)
onSettled: invalidate(['feed'])Mentions/Hashtags and Timestamp Parsing
Pattern-matching and formatting snippets are intentionally moved to the optional LLD section at the end.
Offline Support
- Cache viewed content
- Queue failed write actions
- Retry queue on reconnect
- Show explicit offline/online state
Memory and Scale Guardrails
- DOM budget via virtualization
- Bound cache size by page count
- Dispose observers/listeners aggressively
- Track Web Vitals and API latency/error budgets
Key Takeaways
- ✓Cursor pagination prevents duplicates/missing items under live inserts.
- ✓Virtualization keeps DOM size bounded, which is key to stable 60fps scrolling.
- ✓Real-time strategy: update counts immediately, but insert new posts via banner to preserve scroll context.
- ✓React Query manages server state + optimistic updates; Zustand manages UI state + offline queue.
- ✓In HLD interviews, clear tradeoff reasoning (not code volume) is what gets scored.
- ✓Stable API/event contracts (IDs, cursors, idempotency) reduce inconsistency during retries and real-time sync.