The Question
FE DesignDesign a Scalable Photo Sharing Web Application
Design a responsive, high-performance photo sharing application similar to Instagram. Focus on the frontend architecture required to handle a massive infinite-scrolling feed of images, a robust image upload workflow with client-side processing, and strategies for achieving excellent Core Web Vitals (LCP/CLS). Discuss how you would handle state synchronization for social interactions (likes/comments), image optimization strategies for various device types, and the balance between SEO requirements and a fluid SPA experience.
React
Next.js
TanStack Query
Zustand
Tailwind CSS
CDN
S3
Service Workers
Web Workers
TypeScript
Questions & Insights
Clarifying Questions
Q: What are the core user actions for the MVP?
Assumption: Users can upload photos, view a chronological feed, like/comment on photos, and view user profiles.
Q: Is SEO a priority for this application?
Assumption: Yes, public photo pages and profiles must be crawlable to drive organic traffic, suggesting an SSR or Hybrid rendering approach.
Q: What is the expected scale of the "Feed" (how many photos per session)?
Assumption: Users may scroll through hundreds of photos; we must prioritize memory management and DOM virtualization.
Q: What are the constraints for photo uploads (size, format, editing)?
Assumption: Max 10MB per file, JPEG/PNG/WebP support, and basic client-side cropping/rotation for the MVP.
Q: Do we need offline support?
Assumption: Basic "Read-only" offline support for previously cached feed items and an "Offline-aware" upload queue.
Crash Strategy
Image Delivery Strategy: The bottleneck is LCP (Largest Contentful Paint) and bandwidth. Use modern formats (WebP/AVIF), responsive image sets (
srcset), and a CDN-based image proxy for dynamic resizing.Feed Performance: Implement a "Virtualized Window" for the infinite scroll feed to keep DOM nodes constant regardless of list size.
Upload Reliability: Use a multi-stage upload process (Client-side compression -> Presigned S3 URL -> Background upload with progress tracking).
State Consistency: Use optimistic updates for high-frequency interactions (Likes/Comments) to ensure the UI feels instantaneous despite network latency.
Elite Bonus Points
LIP (Low-Information Previews): Use BlurHash or ThumbHash encoded in the initial JSON response to show a colorful blurred placeholder before the image bytes arrive.
Priority Loading: Implement a custom hook to detect "critical path" images (first 2 items in feed) and set
loading="eager" and fetchpriority="high" for them.Network-Aware Loading: Adjust image quality/resolution requests dynamically based on the user's effective connection type (ECT) via the
navigator.connection API.Design Breakdown
Requirements
Functional Requirements:
Secure User Authentication.
Photo Upload with client-side preview and basic transformations (crop/rotate).
Infinite Scroll Feed with Like and Comment capabilities.
User Profile view showing a grid of uploaded photos.
Non-Functional Requirements:
Performance: LCP < 2.5s on 4G; TTI (Time to Interactive) < 3s.
Scalability: Support smooth 60fps scrolling for feeds with 1000+ items.
Accessibility: WCAG 2.1 Level AA compliance (alt text for images, keyboard navigation for modals).
Responsiveness: Mobile-first design; adaptive layouts for Desktop/Tablet.
Design Summary
Concise Summary: A React-based SPA with SSR capabilities for SEO, utilizing a decentralized state strategy (TanStack Query) to manage heavy server-side image metadata and local caching.
Major Components:
App Shell: Handles global layout, navigation, and authentication persistence.
Image Proxy Service: (Infrastructure) An abstraction to generate optimized image URLs with specific dimensions/formats.
Virtual Feed Container: Manages the logic for windowing and infinite scroll data fetching.
Upload Orchestrator: Manages the lifecycle of an upload, including compression, progress, and retry logic.
CUJ Walkthrough:
Viewing Feed: App Shell renders -> Layout fetches Feed Metadata via Application Layer -> Virtual Feed renders
PhotoCard components -> Images load lazily from CDN.Uploading: User selects file -> Domain Layer compresses image -> Infrastructure fetches Presigned URL -> Upload proceeds in background while User continues browsing.
Simplicity Audit: Avoids complex global state (Redux) in favor of Server-State management (Query), keeping the "source of truth" in the API/Cache.
Architecture Decision Rationale:
Why this architecture?: Decoupling the Image Proxy logic from the UI components allows for easy provider swaps (e.g., moving from Cloudinary to an in-house Sharp-based service) without touching the Presentation Layer.
Requirement Satisfaction: SSR meets SEO needs; Virtualization meets performance needs; Optimistic UI meets responsiveness needs.
System Diagram
Architecture Deep Dive
Presentation Layer
Component Hierarchy: The App Shell wraps the application, providing the
QueryClientProvider and AuthContext. The Layout defines the responsive grid. Pages (Feed, Profile) act as entry points, delegating data management to Feature Containers like the Virtual Feed Container.Interaction Layer: Input validation for comments occurs locally before hitting the API. The Like Button uses a "toggle" animation with CSS transforms for performance.
Rendering Layer: Hybrid approach. The first page load of a Profile or Photo is SSR (using Next.js or similar) for SEO. Subsequent navigation is CSR. The Feed uses Virtualization (via
react-window or virtuoso) to ensure only ~10 PhotoCard components are in the DOM at any time.UI Frameworks / Tools: React for UI, Tailwind CSS for styling (atomic CSS minimizes bundle size), and Framer Motion for subtle transitions between routes.
Application Layer
Data Fetching Layer: Uses TanStack Query for staggered fetching. It handles "Infinite Loading" by tracking
pageParam. It also supports "Stale-While-Revalidate" to show cached photos instantly.State Management Layer: Minimal global UI state (e.g., "isUploadModalOpen") using Zustand. Business data (photos, comments) lives in the TanStack Query cache.
Routing & Navigation: Nested routes (e.g.,
/p/:id for photo modals that can be deep-linked). Navigation guards prevent unauthenticated users from accessing the upload page.Domain Layer
Business Rules: The Image Processor Service handles client-side resizing (using
canvas or browser-image-compression) to save user bandwidth. Validation rules ensure only supported MIME types are processed.Entities / Models: Standardized
Photo and User types ensure the UI isn't tightly coupled to API response naming (DTO mapping).Inter-layer Contracts: Domain services are injected or imported into Feature Containers, keeping the Presentation layer focused on "View" logic.
Infrastructure Layer
API / Network: RESTful API for metadata. To handle photo uploads, the infra layer first requests a Presigned URL, allowing the client to upload directly to S3/GCS, bypassing the main application server to reduce load.
Storage: IndexedDB is used via
persistQueryClient to allow the user to see their feed even when offline. LocalStorage stores the JWT/Auth tokens.Wrap Up
Wrap-up
Evaluation: The design balances extreme performance (virtualization, CDN proxy) with MVP simplicity (Zustand, TanStack Query).
Trade-offs: We chose SSR/CSR Hybrid over pure SPA to solve the SEO requirement, which adds infrastructure complexity.
Future Scale: As the app grows, we could implement GraphQL to allow the Profile page to request only thumbnails while the Feed requests full-size images, further optimizing data transfer.