Back to System Design
System Design
hard
mid

How would you design an Instagram style feed page?

Vertical infinite-scroll feed of posts (image/video, caption, likes, comments). Cursor-paginated, virtualized for memory; aggressive image/video lazy-load with placeholders and autoplay-on-visible; optimistic likes/comments; stories carousel above feed; pull-to-refresh; double-tap-to-like; SSR/SSG home for SEO; service worker for offline shell.

6 min read·~30 min to think through

An Instagram-style feed is a polished example of patterns from [[frontend-system-design-design-a-news-feed-facebook]] — vertical media-heavy posts with very specific interaction details. The interview wants both the architecture and the Instagram-specific UX choices.

1. Feed mechanics

  • Cursor-paginated server feed (offset breaks under inserts).
  • IntersectionObserver sentinel near the bottom triggers next-page fetch.
  • Virtualization for memory — discard far-off-screen posts.
  • Pull-to-refresh to fetch new posts above current top; insert without auto-shifting (show "N new posts" pill if user is scrolled).

2. Post structure

ts
<Post>
<Header> avatar, username, ⋯ menu
<Media> image / video / carousel
<Actions> like, comment, share, save
<LikesCount>
<Caption> username + truncated text
<Comments> top 2 + "View all 142 comments"
<Timestamp>

Memoize so liking one post doesn't re-render the feed.

3. Media — autoplay on visible

For video posts:

  • muted, playsinline, autoplay when ≥ 50% in viewport (IntersectionObserver).
  • Pause when scrolled away.
  • Aspect-ratio reserved for both images and videos to prevent CLS.
  • HLS / DASH for non-trivial video (see [[frontend-system-design-design-video-streaming-netflix]]).
  • Image carousels (multi-image posts) — horizontal scroll-snap, pagination dots, lazy-load slides.

4. Likes — double-tap + optimistic

  • Double-tap on media → like (with the iconic heart-burst animation).
  • Optimistic — bump count, fill heart, send mutation in background, rollback on failure.
  • Idempotency key so retries don't double-like.

5. Comments

  • Show first 1–2 + "View all" expanding inline or on tap.
  • Compose box with submit; optimistic insertion at top.
  • Replies threaded (collapse/expand).

6. Stories carousel

A horizontal strip at the top — separate component, separate data source. Tap into a story → modal/route with stories viewer (tap-and-hold to pause, swipe between users, auto-advance).

7. State & data

  • React Query keyed on (feed, cursor) so back-nav restores scroll + data.
  • UI state (story modal open, comments expanded) local.
  • Caches invalidated on new-post creation.

8. Rendering strategy

  • SSR/SSG home for SEO and fast first paint (LCP = first post's image).
  • Hydrate for interaction.
  • Subsequent navigation is client-side.

9. Performance

  • LCP = first visible image — preload it (Next <Image priority>).
  • Image optimization — responsive srcset, AVIF/WebP, blurhash placeholders.
  • Code-split heavy paths (stories viewer, DMs).
  • Memoize posts; transform-only for any tap animations.

10. Real-time

  • WebSocket for new likes/comments on visible posts (count updates).
  • Optimistic-vs-broadcast reconciliation — don't bump a count twice if your own action echoed back.

11. Resilience

  • Network drop → retry transparently; queue mutations (likes, comments).
  • Service worker for offline shell (cached feed page renders even offline; new posts gated on network).

12. Accessibility

  • Post as <article> with descriptive name.
  • Image alt text (server-supplied; allow user-supplied for posts they upload).
  • Video captions / subtitles where available.
  • All interactive icons have labels ("Like this post by @anya").
  • Live region announces new likes/comments while scrolled.

Interview framing

"Vertical infinite-scroll feed, cursor-paginated, virtualized once long. Each post is a memoized component; videos autoplay muted on ≥50% visibility via IntersectionObserver and pause when out. Double-tap-to-like is optimistic with idempotency; likes/comments update locally first, sync to server, rollback on failure. Stories are a horizontal strip with their own viewer. Pull-to-refresh fetches new posts but doesn't auto-shift — surfaces a 'N new posts' pill instead. SSR/SSG the home page for LCP and SEO. The dominant perf cost is media — modern formats, responsive sizes, blurhash placeholders, preload the LCP image."

Follow-up questions

  • Why cursor pagination instead of offset?
  • How do you autoplay only the visible video?
  • Why surface a 'New posts' pill rather than auto-inserting?
  • How does optimistic like work end-to-end?

Common mistakes

  • Offset pagination — duplicates as new posts arrive.
  • Auto-shifting the scroll when new posts arrive.
  • All videos playing at once.
  • No memo on posts → like → re-render the world.
  • No CLS reservation on media.

Performance considerations

  • Media dominates. Autoplay only the visible video. Memoize posts. Virtualize once feed is long. Preload LCP image. Service worker shell for repeat loads.

Edge cases

  • Network drop mid-scroll.
  • Carousel post with 10 images.
  • User scrolls fast past unloaded video.
  • Two devices, same user, both liking.

Real-world examples

  • Instagram, TikTok, Twitter/X.

Senior engineer discussion

Seniors prioritize media optimization and IntersectionObserver-driven autoplay/pause, design optimistic interactions with idempotency, and treat 'new posts' as a UX event (pill), never a scroll-shift.

Related questions