---
title: "Rendering strategies for retail: ISR vs SSR vs static"
description: Choose between SSR, SSG, ISR, and CSR for each ecommerce surface. Compare rendering modes on freshness, personalization, SEO, and cost.
url: "https://vercel.com/kb/guide/ssr-vs-ssg-vs-isr-vs-csr-for-ecommerce"
published: 2026-10-01
last_updated: 2026-10-01
authors: Vercel
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

A storefront with tens of thousands of stock-keeping units (SKUs) rarely runs on a single rendering mode. A product detail page (PDP) needs indexable HTML at catalog scale and tolerates a few seconds of price staleness, while a checkout inventory check tolerates none. Static Site Generation (SSG), Server-Side Rendering (SSR), [Incremental Static Regeneration](https://vercel.com/docs/incremental-static-regeneration) (ISR), and Client-Side Rendering (CSR) each address a different requirement, so the choice belongs to the surface rather than to the site.

Here's how the mapping works, starting with the surface-by-surface answer and ending with what peak traffic does to it.

## Which rendering mode fits each ecommerce page?

Three properties of a surface decide its mode. Data freshness sets how long a cached response stays correct and is usually tied to publication events in a content management system (CMS). Personalization decides whether one response can be shared between visitors, and indexability decides whether search engines can read the page at all.

Match each storefront surface to a primary mode:

| Ecommerce surface          | Primary mode                                            | Personalization layer                                                                       | Reasoning                                                                                                                       |
| -------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Product detail page        | ISR, with on-demand revalidation on price and inventory | CSR, or a [Partial Prerendering](https://vercel.com/docs/partial-prerendering) (PPR) region | Search-indexed at catalog scale, and seconds of price staleness are tolerable                                                   |
| Category and listing page  | ISR for the indexed category URLs                       | SSR or CSR for filters and facets                                                           | Category URLs are stable and indexed, while filter combinations are unbounded                                                   |
| Search results page        | SSR, or CSR, where results aren't indexed               | Handled in the primary mode                                                                 | Every query is distinct, so there is nothing to cache and nothing to pre-render                                                 |
| Cart and checkout          | CSR for the cart interface, SSR for inventory checks    | Handled in the primary mode                                                                 | Session-scoped and never indexed, with no tolerance for stale inventory                                                         |
| Homepage and campaign page | ISR, or SSG, when content changes only on deploy        | PPR or CSR below the fold                                                                   | Search-indexed and receives high traffic, and [CMS](https://vercel.com/docs/integrations/cms) publishes set the refresh cadence |

A storefront runs several of these modes simultaneously, and the boundaries shift as the catalog grows.

[Routing Middleware](https://vercel.com/docs/routing-middleware) carries the personalization that sits on top of static and ISR pages. Geo-targeting and A/B variants are resolved before the response leaves the content delivery network (CDN), so each visitor receives a pre-rendered variant rather than a page that rewrites itself after load. In Next.js 16, this runs through the [`proxy`](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) [file convention](https://nextjs.org/docs/app/api-reference/file-conventions/proxy).

PPR splits a single page instead of a single site. On a PDP, this puts the product shell in the cache and leaves the cart count, delivery estimate, and recommendations to render on demand.

## What SSG, SSR, ISR, and CSR do in Next.js

The four modes differ in when HTML is generated, what you can do with the output, what it costs, and which API expresses it:

| Dimension                            | SSG                                 | SSR                                     | ISR                                                       | CSR                                           |
| ------------------------------------ | ----------------------------------- | --------------------------------------- | --------------------------------------------------------- | --------------------------------------------- |
| When HTML is generated               | Build time                          | Every request                           | Build time or first request, then background regeneration | In the browser                                |
| Function runs per request            | No                                  | Yes                                     | Only on cache miss or revalidation                        | No, though data APIs still run                |
| CDN-cacheable                        | Yes, with no configuration          | Only with manual `Cache-Control`        | Yes, managed                                              | Static shell only                             |
| Indexable HTML                       | Yes                                 | Yes                                     | Yes                                                       | No                                            |
| Supports personalization             | No                                  | Yes                                     | No, the cache is shared                                   | Yes, client-side                              |
| Time to First Byte (TTFB)            | Fast, served from cache             | Slowest, renders per request            | Fast, served from cache                                   | Fast, but content paints later                |
| Compute cost                         | Lowest, no runtime compute          | Highest, one invocation per request     | Low, invocations only on revalidation                     | Low, plus the data requests the browser makes |
| Pages Router API                     | `getStaticProps`                    | `getServerSideProps`                    | `getStaticProps` with `revalidate`                        | None, fetch in the component                  |
| App Router, without Cache Components | `fetch` with `force-cache`          | `fetch` with `no-store`, or `cookies()` | `next: { revalidate: N }`                                 | Client Components                             |
| App Router, with Cache Components    | `use cache` with `cacheLife('max')` | Uncached data inside `<Suspense>`       | `use cache` with a `cacheLife` profile                    | Client Components                             |

The three API rows express the same four modes, and what each mode does underneath doesn't change between them.

### SSG (Static Site Generation)

SSG renders HTML once, during `next build`, through `getStaticProps` in the Pages Router or a `use cache` function in the App Router. The output is cached on the CDN with no configuration. Build-time rendering has no access to the incoming request, so SSG gives you no personalization and no fresh data between deploys.

### SSR (Server-Side Rendering)

SSR renders on every request, which buys full personalization at the slowest TTFB and the highest compute cost. Without Cache Components, a single request-time API such as `cookies()`, `headers()`, or `searchParams` anywhere in an App Router route opts the entire route into dynamic rendering.

### ISR (Incremental Static Regeneration)

ISR renders a page on first request or at build time, then serves that cached copy and regenerates it in the background after a time interval or an on-demand trigger. Under this stale-while-revalidate pattern, only the first visitor to an uncached path waits on a render.

On Vercel, the [CDN](https://vercel.com/docs/cdn) layers three behaviors on top of that pattern:

- **Request collapsing:** On a cache miss, concurrent requests to the same path [collapse](https://vercel.com/docs/incremental-static-regeneration/request-collapsing) into one function invocation per region, which prevents cache stampedes.
  
- **Cache shielding:** On a CDN miss, Vercel reads from the durable ISR cache before invoking your function.
  
- **Globally consistent purging:** When you revalidate, caches in all regions [update within 300ms](https://vercel.com/docs/incremental-static-regeneration), and HTML and data payloads purge together.
  

All three apply to any ISR route with no per-route configuration.

### CSR (Client-Side Rendering)

CSR ships a minimal HTML shell and lets the browser fetch and render the data. That's the right layer for a cart drawer or an image gallery, and the wrong one for indexable content. Most [AI crawlers](https://vercel.com/blog/the-rise-of-the-ai-crawler#javascript-rendering-capabilities) fetch pages without executing JavaScript, so client-rendered content is invisible to them.

### PPR (Partial Prerendering)

PPR combines ISR and SSR on a single page. An ISR-backed static shell ships from the CDN while [Vercel Functions](https://vercel.com/docs/functions) render the dynamic parts and stream them into the same response. Next.js 16 removed the `experimental.ppr` flag and the `experimental_ppr` route segment config, and PPR is now part of Cache Components.

Enable it in your Next.js config:

```tsx
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
};

export default nextConfig;
```

Most PPR requests incur a function invocation even when the shell is fully cached, so a page with no dynamic content belongs on plain ISR instead.

## How ISR handles catalog scale without full rebuilds

ISR decouples build time from catalog size. Page count stops determining how long a deploy takes, and the freshness of any given page becomes a revalidation setting rather than a rebuild.

### Where full static builds stop scaling

Static generation is the fastest option for a small catalog, and it stops scaling at a predictable point. Every SSG page renders during the build, so build time grows with page count. At 100,000 pages, a full rebuild triggered by one product change is no longer practical.

Two catalogs show the difference:

- **Desenio:** Full static builds across their multi-market catalog would have taken hours. With ISR, [builds take minutes](https://vercel.com/customers/how-vercel-helped-desenio-future-proof-their-business).
  
- **Mecum Auction:** [275,000 lot pages](https://vercel.com/customers/managing-275-thousand-pages-and-8-million-assets-with-isr) and 8 million assets run without pre-generating any of the lot pages. Each page renders on first request and stays cached.
  

You don't have to choose between the two. Selective pre-rendering builds your highest-traffic pages at deploy time and generates the rest on demand.

### How to set ISR revalidation intervals

Regenerating pages more often than their content changes raises cost without improving freshness. [Short ISR intervals](https://vercel.com/blog/technical-audits) drove up infrastructure costs for one enterprise team. Extending those intervals alongside on-demand revalidation improved [cache hit rates](https://vercel.com/docs/caching/cache-status) on the same project.

Set a long time-based revalidation window, an hour rather than a second, then layer on-demand revalidation on top of it through `revalidatePath` or `revalidateTag` tied to CMS publish webhooks. Next.js applies the lowest `revalidate` value across a route, so a single `fetch` with `revalidate: 0` or `cache: 'no-store'` makes the whole route dynamic and erases every ISR benefit.

### How Cache Components changes ISR in Next.js 16

Enabling `cacheComponents` inverts the defaults. Data is dynamic unless you cache it, and the route segment configs `dynamic`, `revalidate`, and `fetchCache` raise an error once the flag is on. Replace `revalidate` with a [`cacheLife`](https://nextjs.org/docs/app/api-reference/functions/cacheLife) profile (`'seconds'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, or `'max'`) inside a [`use cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache) function.

On-demand revalidation changes too, since `revalidateTag` now takes a cache profile as its second argument.

Refresh a single PDP from an inventory webhook:

```tsx
import { revalidateTag } from 'next/cache';

export async function POST(request: Request) {
  const secret = request.headers.get('x-webhook-secret');

  if (secret !== process.env.INVENTORY_WEBHOOK_SECRET) {
    return new Response('Unauthorized', { status: 401 });
  }

  const { productId } = await request.json();
  revalidateTag(`product-${productId}`, 'max');

  return Response.json({ revalidated: true });
}
```

The route sits at a public URL, so the secret check runs before any revalidation work. Each revalidation bills an ISR write and a function invocation, and the check is what keeps that spend tied to real inventory changes.

For a mutation the shopper has to see immediately, such as their own cart update, use `updateTag` from a Server Action instead, which expires the tag so the next request waits for fresh data. The [Cache Components migration guide](https://nextjs.org/docs/app/guides/migrating-to-cache-components) covers the rest of the config changes.

### How ISR works in SvelteKit, Nuxt, and Astro

ISR isn't limited to Next.js. Each supported framework exposes its own configuration for it:

- **SvelteKit:** Export `config` with an `isr` property.
  
- **Nuxt:** Add `routeRules` with the `isr` option.
  
- **Astro:** Configure server output with ISR.
  

Vercel applies the same caching, request collapsing, and revalidation to each of them with no extra configuration.

## How each rendering mode affects Core Web Vitals and conversion

TTFB and Largest Contentful Paint (LCP) are downstream of the rendering choice. SSG and ISR pages leave the CDN immediately, while an uncached SSR route renders on every request and has the slowest TTFB, which consumes more of the LCP budget before a pixel paints.

Google's [Core Web Vitals](https://vercel.com/kb/guide/why-site-performance-matters) thresholds are an LCP of 2.5 seconds or less, an Interaction to Next Paint (INP) under 200ms, and a Cumulative Layout Shift (CLS) below 0.1. In June 2025, [48% of mobile pages](https://almanac.httparchive.org/en/2025/seo) reached good scores across all three, against 56% on desktop.

Helly Hansen measured the difference directly. Their storefront ran as a client-rendered Adobe Commerce progressive web app (PWA) whose personalization and A/B scripts blocked rendering and caused layout shifts. Moving from client-side to server-side rendering [improved LCP by 30%](https://vercel.com/customers/how-helly-hansen-migrated-to-vercel-and-drove-80-black-friday-growth), cut load times from over 4 seconds to a consistent 1.8 to 2 seconds, virtually eliminated a CLS score above 2.8, and turned Core Web Vitals from all red to all green within five months. Conversions rose more than 30%.

A/B testing is the CLS risk that hides inside that pattern. Client-side experiment frameworks swap content after first paint, which registers as a layout shift. Running experiments through Routing Middleware avoids the swap, since the variant is resolved before the page reaches the browser.

## How rendering decisions hold up under peak traffic

Black Friday and Cyber Monday (BFCM) test the rendering boundaries you drew months earlier. Over the four days of [BFCM 2025](https://vercel.com/blog/bfcm-2025), Vercel served 115.8 billion requests and peaked at 518,027 requests per second. Of those, 56.9 billion were served directly from cache, and ISR processed 1.8 billion reads and 1.5 billion writes.

Request collapsing is what keeps cold paths alive at that volume, and it works because ISR routes declare their cacheability at build time. Vercel distributes route metadata to every CDN region on deploy, so a region knows a path is cacheable before the first request arrives. `Cache-Control` headers alone can't do this, since Vercel only learns a response is cacheable after the origin replies.

A peak also tests what happens when revalidation fails. Vercel keeps serving the cached page and sets a 30-second time-to-live before retrying, so a broken upstream doesn't turn into a blank page.

Three retailers have published their peak-weekend results:

- **Helly Hansen:** [80% year-over-year](https://vercel.com/customers/how-helly-hansen-migrated-to-vercel-and-drove-80-black-friday-growth) Black Friday growth with zero downtime, and a 2x lift in conversion rates over the weekend.
  
- **PAIGE:** [22% more traffic](https://vercel.com/blog/how-paige-grew-revenue-by-22-with-shopify-next-js-and-vercel) with no downtime, a 76% increase in conversion rates, and 22% more revenue across BFCM.
  
- **KIKO Milano:** [Three weeks](https://vercel.com/customers/how-kiko-milano-scales-for-black-friday) of manual Black Friday infrastructure prep removed, with build times down 75% and releases going out several times a day.
  

Whichever modes you land on, freeze them ahead of the traffic ramp rather than during it.

## Next steps

Start with ISR on every catalog page that needs to rank, layer CSR over it for interactive elements, reserve SSR for surfaces where data has to be request-fresh, and move to PPR where a ranked page also has to stay individual to the visitor.

[Start a new project](https://vercel.com/new) to configure revalidation against your own catalog, or work from the [headless Shopify storefront](https://vercel.com/kb/guide/deploy-headless-shopify-storefront-with-vercel) guide if you want these boundaries already drawn.

## Read more

- [Incremental Static Regeneration (ISR)](https://vercel.com/docs/incremental-static-regeneration)
  
- [Partial Prerendering (PPR)](https://vercel.com/docs/partial-prerendering)
  
- [How to reduce ISR revalidation costs](https://vercel.com/kb/guide/how-to-reduce-isr-revalidation-costs)
  
- [Why site performance matters](https://vercel.com/kb/guide/why-site-performance-matters)
  
- [How to improve Core Web Vitals](https://vercel.com/kb/guide/how-to-improve-core-web-vitals)
  
- [How to choose the best rendering strategy for your app](https://vercel.com/blog/how-to-choose-the-best-rendering-strategy-for-your-app)
  

## Frequently asked questions

### Is ISR better than SSR for product detail pages?

ISR is the default. SSR is worth its cost only when every request needs user-unique data, such as personalized pricing, or when inventory can't tolerate even seconds of staleness. For everything else on a PDP, on-demand revalidation keeps the page fresh enough at a fraction of the compute.

### Can you use SSG for a large product catalog?

Yes, for as long as a full rebuild on every product change stays acceptable. The rebuild is the real limit, not the rendering. Once it's too slow to run on a catalog update, ISR produces the same cached HTML per page and skips the rebuild entirely.

### Does ISR work the same way in the App Router and the Pages Router?

The APIs differ, though the caching behavior doesn't. Pages Router ISR returns a `revalidate` property from `getStaticProps`, while the App Router sets `revalidate` on a route segment or passes `next: { revalidate: N }` to `fetch`. Next.js 16 replaces both with `use cache` once Cache Components is on.

### What happens if you set an ISR revalidation interval too short?

You pay for regeneration you get no freshness from. A one-second window regenerates on close to every request, and the ISR writes and function invocations bill the same whether the underlying data changed or not. On-demand revalidation covers freshness, which is what lets the time window stay long.

### How is Partial Prerendering different from ISR?

ISR treats the page as one cache unit that is either fully cached or fully regenerated. PPR splits it at the Suspense boundary, so the cached shell and the per-request regions travel in a single response. Use ISR when nothing on the page is personalized, and PPR when a page has to paint fast and stay individual.

## More Next.js guides

- [How to set up local HTTPS for Next.js and other dev servers](/kb/guide/access-nextjs-localhost-https-certificate-self-signed): Learn when you need local HTTPS, how to enable it with next dev --experimental-https, and how to clear certificate warnings on localhost.
- [A/B testing on ecommerce storefronts without hurting Core Web Vitals](/kb/guide/ecommerce-ab-testing-core-web-vitals): Client-side A/B tools can worsen LCP and CLS on the pages you're testing. Run ecommerce A/B tests at the edge with Routing Middleware and Vercel Flags.
- [Making your catalog discoverable to AI agents](/kb/guide/agentic-commerce-readiness): Agentic commerce readiness starts with the initial HTML response. Server-render your product catalog, ship JSON-LD, and verify AI agents can read it.