← Back to All Analyses
Frontend & Next.js•7 min read•

Next.js App Router & SSR vs. SPAs: Architecture Lessons from Scaling E-Commerce to 10,000+ SKUs

How Server-Side Rendering, edge caching, and atomic component boundaries deliver sub-350ms TTFB across high-traffic digital retail platforms.

Pawan Pratap
Pawan PratapFounder & Lead Architect

1. The Hidden Cost of Client-Side Single Page Apps

For nearly a decade, engineering teams defaulted to bundling massive single-page applications (SPAs) with Create React App or standard Vite configurations. While fine for private internal dashboards, SPAs are fundamentally flawed for public e-commerce.

When a customer on an Android device over a 4G connection visits a client-rendered catalog with 10,000 items, three failure modes occur simultaneously:

  1. CPU Starvation: The browser must download a 1.2MB JavaScript bundle, parse it, execute runtime React reconciliation, and then trigger client-side REST calls.
  2. Search Engine Abandonment: Crawlers from Google, Bing, and AI search engines encounter empty root <div id="root"></div> wrappers, degrading indexing quality.
  3. Severe Cumulative Layout Shift (CLS): As dynamic product images and pricing load asynchronously, the DOM shifts violently, failing Core Web Vitals.

2. React Server Components as the Default Baseline

With Next.js App Router and React Server Components (RSC), we shift the computational burden off the buyer's smartphone and onto high-performance edge compute clusters.

On platforms like JJ Mart, product grid pages fetch directly from PostgreSQL within the server execution context. Zero JavaScript from database libraries, markdown parsers, or formatting utilities is sent down the wire to the browser:

// Server Component: Zero client bundle overhead
export default async function ProductCatalogPage({ searchParams }: PageProps) {
  const { category, page = "1" } = await searchParams;
  
  // Direct database query on edge worker
  const products = await getFilteredProducts({ category, page: parseInt(page) });

  return (
    <main className="catalog-shell">
      <h1>{category ? formatCategoryTitle(category) : "All Products"}</h1>
      <div className="product-grid">
        {products.map((item) => (
          <ProductCard key={item.id} product={item} />
        ))}
      </div>
      {/* Interactive client island only where user state is needed */}
      <Suspense fallback={<CartSkeleton />}>
        <FloatingCartDrawer />
      </Suspense>
    </main>
  );
}

3. Incremental Static Regeneration & Edge Tag Purging

A catalog of 10,000 SKUs cannot be regenerated statically on every single product update. Building all 10,000 pages during deployment would take 20 minutes.

Instead, we leverage On-Demand Cache Tag Revalidation. Product detail pages are cached at edge CDN locations until a webhook notifies our application of price or stock adjustments:

// Triggered by ERP webhook upon inventory update
export async function POST(req: Request) {
  const { sku, updatedStock } = await req.json();
  
  await updateDatabaseInventory(sku, updatedStock);
  
  // Invalidate specific SKU edge cache instantaneously
  revalidateTag(`product-${sku}`);
  revalidateTag('catalog-listing');
  
  return Response.json({ revalidated: true, now: Date.now() });
}

4. Real-World Lighthouse Benchmarks

By enforcing Server Component boundaries and zero-runtime CSS modules, our production storefronts achieve perfect Core Web Vitals:

  • Largest Contentful Paint (LCP): 0.8 seconds (Google threshold: < 2.5s)
  • Interaction to Next Paint (INP): 42 milliseconds (Google threshold: < 200ms)
  • Cumulative Layout Shift (CLS): 0.000 (Zero layout jump)
  • Lighthouse Mobile Score: 99/100 across 4G simulated throttling

5. Production Architecture Checklist

Before launching your next web platform, audit against these 4 non-negotiable standards:

  1. Keep interactive "use client" directives at the leaf node level (buttons, modals, accordions)—never at the layout or root page level.
  2. Inline critical fonts using next/font with display: "swap" to prevent flash of unstyled text.
  3. Serve optimized WebP/AVIF images with explicit width and height constraints.
  4. Audit bundle size with @next/bundle-analyzer on every production pull request.

Building a Similar Architecture?

Discuss user flows, database models, and WhatsApp agent pipelines directly with Pawan Pratap. Zero middlemen.

Schedule Technical Scoping→