Design E-commerce Product Page
Frontend system design for a PDP — gallery, variants, ATC, inventory, SEO/SSR, performance, and trust UI.
- system-design
- interview
- architecture
- ecommerce
Scope the problem
In scope: Product detail page (PDP) frontend — media gallery, variant selection (size/color), price, add-to-cart, reviews teaser, SEO, performance.
Out of scope: full checkout, warehouse inventory algorithms, ad retargeting platform.
Assumptions: SKU matrix with options; mobile-heavy traffic; SSR/SSG for SEO.
Critical user journey
Land on PDP → understand product → pick variant → ATC → (mini-cart / cart)
Optimize for time-to-ATC and trust (reviews, shipping, returns).
Page architecture
┌─────────────────────────────────────────────────────┐
│ SSR shell: title, price range, JSON-LD Product │
├─────────────────────┬───────────────────────────────┤
│ Media gallery │ Buy box │
│ (LCP image) │ title, price, variants, ATC │
│ │ shipping promise │
├─────────────────────┴───────────────────────────────┤
│ Accordion: details, specs │
│ Reviews (deferred) │
│ Recommendations (deferred) │
└─────────────────────────────────────────────────────┘
Route-split reviews and recommendations.
Data model
type Product = {
id: string;
slug: string;
title: string;
descriptionHtml: string;
options: { name: string; values: string[] }[]; // Color, Size
variants: Variant[];
media: { id: string; url: string; alt: string; type: "image" | "video" }[];
rating?: { average: number; count: number };
};
type Variant = {
id: string;
sku: string;
optionValues: Record<string, string>; // { Color: "Red", Size: "M" }
priceCents: number;
compareAtCents?: number;
available: boolean;
imageId?: string;
};
Selection state: map of option → value; resolve matching variant; if impossible combination, show unavailable.
Buy box UX
- Selecting color updates gallery primary image
- Size stock: disable OOS with explanation
- Price updates with variant
- ATC disabled until required options chosen
- Quantity stepper
- Error: inventory race (“only 2 left”) on ATC response
async function addToCart(variantId: string, qty: number) {
// optimistic mini-cart badge
// rollback on 409 conflict
}
Cart drawer: Shopping Cart Drawer.
Rendering strategy
| Part | Strategy |
|---|---|
| Core PDP | SSR/SSG + revalidate (ISR) |
| Price/availability | may stream or client refresh if highly dynamic |
| Reviews | client infinite query below fold |
| Recs | personalized client fetch; privacy mode |
SEO: semantic HTML, Product JSON-LD, canonical URL, OG image from primary media. Avoid pure client-only PDP for indexable catalog.
Performance
- LCP: primary image high priority; correct dimensions; CDN modern formats
- Gallery thumbs lazy
- JS: buy box essential; defer reviews widgets from third parties (often the real problem)
- Prefetch cart chunk on ATC intent
- CLS: reserve image aspect; reserve rating skeleton
Budgets example: PDP route JS < 150–200KB gzip interactive path.
Caching
| Layer | Notes |
|---|---|
| CDN HTML | cache public PDPs; stale-while-revalidate |
| Variant stock | short TTL or on-demand check at ATC |
| Images | long cache immutable derivatives |
| Client | React Query product by slug |
Personalized pricing (member deals) fragments cache — edge auth or client overlay.
Trust & a11y
- Clear focus on options (radio groups / listboxes)
- Images alt from product
- Error messages linked to controls
- Don’t convey OOS by color alone
- Honest shipping dates
Tradeoffs
- SSG freshness vs stock accuracy
- Client vs server variant matrix for huge option spaces
- Third-party reviews vs first-party (perf vs features)
- SPA soft-nav between PDPs vs full document for analytics simplicity
Interview close
SSR product shell + buy box state machine for variants → ATC optimistic with inventory errors → deferred reviews/recs → image/CDN LCP → cache vs personalization. Mention cart drawer and analytics events (view_item, add_to_cart).
Variant matrix edge cases
- Options that are incompatible (Red sold out in XL only)
- Single remaining SKU preselect
- Backorder vs OOS copy
- Region-specific catalogs (URL locale + currency)
Model availability on the variant, not only product level.
Analytics events
view_item → select_item (variant) → add_to_cart → begin_checkout
Include variant_id, price, and currency. Fire once per meaningful change; debounced variant browsing shouldn’t spam.
Third-party widgets
Reviews, size guides, and chat often destroy LCP/INP. Load after idle or on interaction; sandbox where possible. Budget their KB in the PDP performance budget explicitly.
Related on this site
- Shopping Cart Drawer
- Media CDN and Image Pipeline
- SSR CSR Islands Architecture
- System Design Interview Framework