ESC

Type to search the knowledge base.

Design E-commerce Product Page

Frontend system design for a PDP — gallery, variants, ATC, inventory, SEO/SSR, performance, and trust UI.

intermediate4 min read
  • 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

  1. SSG freshness vs stock accuracy
  2. Client vs server variant matrix for huge option spaces
  3. Third-party reviews vs first-party (perf vs features)
  4. 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.

Further reading