Frontend system design: design Photo Sharing (Instagram)
Photo-first social product: feed (see [[design-an-instagram-like-feed-page]]) + upload flow + profile + explore. Upload: client crops/filters → direct-to-S3 pre-signed URL → post-process server-side → create post referencing uploaded media. Heavy image optimization (responsive srcset, modern formats, blurhash). Stories (24h ephemeral), DMs, search/explore powered by server.
Instagram is feed + upload + profile + explore + stories + DMs. The interview wants the architecture across all surfaces, plus a deep dive on the photo upload pipeline (which is distinctive).
1. Surfaces
- Feed — see [[design-an-instagram-like-feed-page]] for the full treatment.
- Upload / Create — pick → crop → filter → share.
- Profile — grid of user's posts, follow stats, bio.
- Explore — algorithmic discovery (server-driven).
- Stories — 24h ephemeral carousel.
- DMs — see [[frontend-system-design-design-a-chat-app-messenger]].
- Reels / video — see [[frontend-system-design-design-video-streaming-netflix]] patterns.
2. Upload pipeline (the distinctive one)
Pick → Crop → Filter (client-side preview) → Upload → Post-process → PublishClient-side editing
- File pick via
<input type="file">(camera or library). - Crop in a canvas; output a normalized image.
- Filters applied via CSS
filterfor preview; the final image can be either: - Server-applied (send original + filter id; server applies for fidelity).
- Client-applied via canvas (faster, but client decides quality).
- Compression — resize to a max dimension before upload (e.g., 1080px wide).
Direct-to-storage upload
Don't proxy bytes through your app server. Direct upload to S3 (or equivalent) via a pre-signed URL:
1. Client → App: requestUploadUrl(mime, size)
2. App → Client: { uploadUrl, mediaId }
3. Client → S3: PUT uploadUrl with file bytes
4. Client → App: createPost(mediaId, caption, ...)Pros: app server stays light; resumable; cheaper.
Resumable / chunked
For larger files (video), use multipart upload with resumability.
Progress + cancel
Show upload progress (XHR onprogress or fetch streams). Allow cancel mid-upload.
Post-processing
After upload, the server:
- Validates and re-encodes.
- Generates multiple sizes for srcset.
- Generates AVIF/WebP variants.
- Generates blurhash / LQIP for placeholders.
- Runs moderation models.
3. Feed (recap)
See [[design-an-instagram-like-feed-page]]. Cursor-paginated, virtualized, optimistic likes, autoplay videos.
4. Profile grid
- 3-column responsive grid of posts.
- Lazy-load on scroll (IntersectionObserver).
- Tap → post detail (route change, deep-linkable).
5. Explore
- Server returns a personalized grid.
- Mixed aspect ratios; masonry-ish layout.
- Infinite scroll, virtualized.
6. Stories
- 24h ephemeral — fetch only stories from followed users.
- Carousel at the top of the feed.
- Viewer: tap to advance, swipe between users, long-press to pause; auto-advance with progress bars per story.
- Mark seen on view; persists.
7. DMs
- Chat patterns from [[frontend-system-design-design-a-chat-app-messenger]].
8. Image optimization (dominant cost)
- Pre-generated sizes on upload (200, 400, 800, 1080, 1440).
- Responsive
srcsetwith appropriatesizes. - AVIF/WebP with JPEG fallback.
- Lazy-load below the fold.
- Blurhash placeholders to avoid blank space.
- Aspect-ratio reserved (Instagram crops to square / 4:5 / 16:9 — known at upload).
9. SEO
- Profile pages and individual post pages SSR for crawlers and link previews.
- Open Graph + Twitter Card metadata per post.
10. Offline / resilience
- Service worker caches the feed shell.
- Uploads queue in IndexedDB if offline; replay on reconnect.
11. Push notifications
- Likes, comments, follows, DMs, story views.
Interview framing
"Instagram is feed + upload + profile + explore + stories + DMs — most patterns recur from a news feed. The distinctive piece is the photo upload pipeline: pick → client crop and filter preview → direct upload to S3 via pre-signed URL (don't proxy bytes through the app server) → server post-processes (resize, AVIF/WebP, blurhash, moderation) → createPost references the mediaId. Show progress, allow cancel, resumable for videos. The feed itself follows the news-feed playbook — cursor pagination, virtualization, optimistic likes — and stories are a separate ephemeral carousel with auto-advancing viewer. Dominant perf cost is media: pre-generated responsive sizes, modern formats, blurhash placeholders. SSR profile and post pages for SEO and link previews."
Follow-up questions
- •Walk through the photo upload pipeline.
- •Why direct-to-S3 instead of proxying through the app server?
- •How do you handle a failed upload mid-way?
- •How is the stories viewer different from feed video autoplay?
Common mistakes
- •Proxying upload bytes through the app server (cost, latency, scale).
- •No pre-generated responsive sizes — phone downloads 4000px images.
- •Stories without auto-advance and tap-to-pause.
- •Auto-scroll feed on new post arrival.
Performance considerations
- •Media dominates — pre-generated sizes, modern formats, blurhash, lazy below-the-fold. Direct upload offloads the app server.
Edge cases
- •Upload over flaky network (resumable).
- •Pre-signed URL expiry mid-upload.
- •User cancels mid-upload (cleanup orphan media).
- •Story expires while a user is viewing it.
Real-world examples
- •Instagram, Pinterest, Snapchat.