Design a Social Media News Feed
System design for a Twitter/Facebook-style news feed: infinite scroll, virtualization, optimistic updates, real-time updates, lazy loading, and content ranking.
Designing a social media news feed challenges your understanding of list performance, real-time updates, and optimistic UI. Here's how to structure your answer in an interview.
Requirements Clarification
Functional Requirements
- Display a chronological or algorithmically ranked feed of posts.
- Infinite scroll: load more items as the user scrolls toward the bottom.
- Each post: author, content (text, images, videos), timestamp, like/comment counts, and actions (like, comment, share).
- User can like, comment, or share; feedback must feel instant.
- Optional: real-time updates when new posts arrive (e.g., "3 new posts" banner).
- Optional: pull-to-refresh on mobile.
Non-Functional Requirements
- Performance: Smooth 60fps scroll; LCP under 2.5s.
- Memory: Virtualize long lists—don't render thousands of DOM nodes.
- Network: Graceful loading states; handle slow connections and retries.
- Perceived performance: Optimistic updates for likes; skeletons for initial load.
High-Level Architecture
┌──────────────────────────────────────────────────────────────┐
│ FeedPage │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ VirtualizedFeed (react-window / @tanstack/react-virtual)│ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ PostCard│ │ PostCard│ │ PostCard│ │ PostCard│ ... │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ NewPostsBanner (when real-time delivers new items) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ useFeed hook: infinite query, mutations, optimistic UI │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
Data flow: initial fetch → render virtualized list → scroll triggers next page → mutations (like/comment) with optimistic updates → optional WebSocket for new posts.
Component Design
FeedPage
Orchestrates the feed. Holds scroll container ref. Renders VirtualizedFeed, NewPostsBanner, and loading/error states. Handles "load more" trigger (intersection observer or scroll listener).
VirtualizedFeed
Uses react-window or @tanstack/react-virtual to render only visible rows + a small buffer. Each row height can be dynamic (variable content) or estimated. Pass posts and onLoadMore callback.
PostCard
Presentational component: avatar, author name, content, media (lazy-loaded), timestamp, action bar (like, comment, share). Receives post and onLike, onComment callbacks. Uses IntersectionObserver for images within the card.
useFeed Hook
- Uses TanStack Query's
useInfiniteQueryfor paginated feed. useMutationfor like/comment withonMutatefor optimistic update (update cache before response).- Optional:
useSubscriptionor WebSocket listener for new posts; store in separate state and show banner.
interface Post {
id: string;
author: { id: string; name: string; avatarUrl: string };
content: string;
media?: { type: 'image' | 'video'; url: string }[];
likesCount: number;
commentsCount: number;
likedByMe: boolean;
createdAt: string;
}
State Management
- Feed items: Server state via TanStack Query. Key:
['feed', cursor]. Pages appended as user scrolls. - Optimistic state: When user likes, immediately update
likedByMeandlikesCountin cache; rollback on error. - New posts (real-time): Client state—array of posts from WebSocket. Merged when user taps "View new" or on refresh.
- Scroll position: Preserve when navigating away (e.g., to post detail) using
sessionStorageor a store so user returns to same position.
API Design
GET /api/feed?cursor=&limit=20
Response: { posts: Post[], nextCursor: string | null }
POST /api/posts/:id/like → { liked: boolean }
POST /api/posts/:id/comments → { comment: Comment }
WebSocket: subscribe to feed channel; receive { type: 'new_post', post: Post }
Cursor-based pagination is preferred over offset for consistency with real-time inserts.
Performance Considerations
- Virtualization: Essential. Only render ~20–30 rows in view + buffer. Use fixed or estimated row heights; dynamic heights need measurement.
- Image lazy loading:
loading="lazy"and/orIntersectionObserverfor images in view. Consider blur placeholder and responsivesrcset. - Content ranking: If ranking happens client-side (e.g., re-sort by engagement), do it in a
useMemowith a stable sort key. Prefer server-side ranking for scale. - Debounce scroll: Throttle scroll handlers; rely on virtualization library's built-in handling.
- Code splitting: Lazy-load heavy components (e.g., video player, rich editor for comments).
Accessibility
- Infinite scroll: Provide a "Load more" button as fallback for keyboard users; or ensure focus management when new items render.
- Feed structure: Use
role="feed"or semantic list; each post asarticle. - Images: Alt text from API or descriptive fallback.
- Reduced motion: Respect
prefers-reduced-motionfor like animation and scroll behavior. - Focus: When opening a post in a modal, trap focus; when closing, return to the post card.
Trade-offs and Extensions
Trade-offs: Infinite scroll vs. pagination—infinite scroll is engaging but hard to "get back" to a specific post; consider hybrid (e.g., "Load more" button). Real-time adds complexity—polling is simpler; WebSockets scale better for many users.
Extensions: Add filters (top, latest, following). Implement "read" tracking. Add bookmark/save. Offline support with service worker. Prefetch next page on approach. Skeleton loaders per post. Image CDN with multiple sizes for responsive loading.
Edge Cases and Consistency
When the user likes a post and the server returns an error, roll back the optimistic update and show a toast so the UI stays consistent. If new posts arrive via WebSocket while the user is scrolled up, show the "N new posts" banner and merge when they tap it—don't auto-scroll and lose their position. Preserve scroll position when navigating to a post detail and back (e.g. with scroll restoration or storing position in session storage). Handle deleted or moderated posts: remove them from the feed and update counts. For ranking changes, avoid jarring reorders on every fetch—consider stable sort keys or only re-rank on full refresh.
Related articles
- System DesignDesign a Search Results Page
System design for a search results page: filters, sorting, facets, infinite scroll vs pagination, and performance at scale.
Read article - System DesignDesign a Comments Thread
System design for nested comments: replies, pagination, real-time updates, and moderation. Frontend architecture and data shape.
Read article - System DesignDesign Autocomplete / Typeahead Search
System design for building a Google-like autocomplete search component: debouncing, caching, keyboard navigation, race conditions, and mobile considerations.
Read article - System DesignDesign a Real-Time Chat Application
System design for Slack/WhatsApp-style chat: WebSocket management, message ordering, offline support, typing indicators, read receipts, search, and file uploads.
Read article