---
title: "How to choose a Salesforce Commerce Cloud storefront: PWA Kit, Storefront Next, or Next.js"
description: Compare PWA Kit, Storefront Next, and a Next.js storefront on Vercel for Salesforce Commerce Cloud. Learn how caching, personalization, SCAPI, deployment, support, and migration effort change across the three options.
url: /kb/guide/salesforce-pwa-kit-vs-storefront-next-vs-nextjs
canonical_url: "https://vercel.com/kb/guide/salesforce-pwa-kit-vs-storefront-next-vs-nextjs"
published: 2026-09-08
last_updated: 2026-09-08
authors: Anshuman Bhardwaj, Neha Julka
related:
  - /docs/deployments/environments
  - /docs/cdn
  - /docs/observability
  - /docs/vercel-firewall
  - /blog/salesforce-incremental-migration
  - /blog/the-no-nonsense-guide-to-composable-commerce/
  - /docs/how-vercel-cdn-works
  - /docs/functions/configuring-functions/region
  - /docs/production-checklist
  - /kb/guide/black-friday-preparation
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

Teams building a Salesforce Commerce Cloud storefront today generally have three paths:

- **PWA Kit** is Salesforce's React storefront kit for headless B2C Commerce. It runs on Managed Runtime and talks to the backend through the Salesforce Commerce API (SCAPI) and the Shopper Login and API Access Service (SLAS).
  
- **Storefront Next** is Salesforce's newer template, built on React Router, for server-rendered and streaming storefronts. It also runs on Managed Runtime.
  
- **A Next.js frontend** reaches the same backend via SCAPI and SLAS and can run anywhere. Rendering, caching, and integrations are decided by the application rather than the platform.
  

On the surface, these can look like three variations of the same React architecture. In practice, they make fundamentally different choices about what can be cached, where shopper state lives, how SCAPI requests are orchestrated, how changes reach production, and how much of the frontend your team owns.

To make those differences concrete, imagine a merchandiser schedules a promotion to begin at noon. A few minutes later, a returning shopper opens a product detail page containing:

- Product copy, images, and category links that rarely change.
  

- Prices, promotions, and inventory that can change throughout the day.
  

- Recommendations based on the shopper’s context.
  

- A cart count and saved items belonging to that shopper.
  

- Page Designer content scheduled without a frontend release.﻿
  

The page must combine stable content, current commerce data, and personalized shopper information without making everything wait on the slowest request. All three approaches can render it, but they provide different levels of control over how each part is cached, personalized, and delivered.

## Before you compare the three

### What to decide before choosing a storefront architecture

Most teams begin this conversation with a framework comparison: React Router vs. Next.js, Vite vs. Turbopack, Managed Runtime vs. Vercel. But the more important question is how much control your team needs over what gets rendered, cached, personalized, and deployed.

These six questions help you with that decision:

1. Which content must be present and correct in the initial HTML response?
   
2. Which content must vary by shopper or request?
   
3. Which parts of a page change infrequently, and which must update throughout the day?
   
4. In which regions are your shoppers, frontend compute, and Salesforce instance located?
   
5. Which Salesforce capabilities and integrations need to remain turnkey?
   
6. Who will own the frontend architecture, session boundary, observability, and production incidents?
   

The answers can help reveal the central architectural tradeoff. The PWA Kit can cache a shared HTML document, but much of the shopper-specific experience is handled by the client. Storefront Next can render personalized content on the server, but it does not cache HTML documents.

Next.js App Router removes the need to make that choice at the page level. A single route can combine cached product content with request-time pricing, inventory, promotions, and shopper state. That gives teams greater control over rendering and caching, while also transferring more integration and operational responsibility to the storefront team. To understand that tradeoff, it helps to first separate the caching layers involved.

### Caching is a coordination problem

Suppose the noon promotion is active in Salesforce, but a shopper still sees the previous price. The stale response could come from several layers: a Commerce API cache, a cached storefront response, an application cache, or data already held by the browser. Refreshing one layer does not automatically refresh the others.

| Cache layer                   | What it stores                                         | Why it matters                                                                                        |
| ----------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| Storefront CDN                | Eligible page responses and static assets              | A cached response avoids another server render, but it must not expose one shopper’s data to another. |
| Application or metadata cache | Data used by the application to assemble a page        | Its lifetime determines when application-level content is recomputed or fetched again.                |
| Browser cache                 | JavaScript, CSS, fonts, images, and other static files | It reduces repeat transfer, but it cannot compensate for a slow document render or commerce request.  |
| SCAPI cache                   | Eligible Commerce API `GET` responses                  | Its lifetime and personalization behavior depend on the endpoint and requested expansions.            |

These caches are controlled and invalidated independently. A product update can therefore be fresh in Salesforce but stale elsewhere in the delivery path.

The architectural question is where to draw the boundary: which parts of the product page can be shared, which must be resolved for each request, and which should wait for the browser? PWA Kit, Storefront Next, and Next.js answer that question differently.

### PWA Kit favors reusable server-rendered HTML

PWA Kit page responses have a documented default cache lifetime of [600 seconds](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/maximizing-your-cache-hit-ratio.html). Salesforce describes the cached server render as a generic foundation that the browser builds on.

For the running PDP, a cache-friendly PWA Kit response can include product copy, navigation, and images. Shopper-specific or fast-changing fields such as price, promotions, availability, cart state, and shopper identity are commonly withheld from the shared server render and filled in by the browser.

In the noon-promotion scenario, the shopper can receive cached product copy and imagery immediately. The browser then fetches the current promotional price, availability, recommendations, and cart state. This protects shopper-specific data from being included in a shared response, but it can also leave visible gaps or loading states while JavaScript hydrates and additional requests complete.

The familiar guard makes the boundary visible:

```jsx
const isBrowser = typeof window !== "undefined";

return isBrowser ? <ProductPrice productId={productId} /> : <PriceSkeleton />;
```

The guard is not inherently bad. It prevents one shopper from receiving another shopper's cached content. But every field moved behind it becomes client work: JavaScript must load, React must hydrate, an API request may run, and the component must render again.

Salesforce's performance guidance explicitly notes that a storefront can have a strong server-side Largest Contentful Paint (LCP) while client JavaScript produces poor Interaction to Next Paint (INP) or Total Blocking Time (TBT). That is a risk to measure when your PWA Kit storefront gets slow. Bundle size, third-party scripts, rerenders, request waterfalls, and component design still matter.

For the merchandising team, the practical question is how quickly the scheduled promotion reaches those client-side requests. For engineering, the question is whether hydration, bundle execution, and request waterfalls still produce an acceptable experience on slower devices and networks.

### Storefront Next favors personalized server-rendered HTML

Storefront Next moves from PWA Kit's client-heavy, hook-driven architecture to React Router 7, React 19, Vite, strict TypeScript, route loaders and actions, and Suspense streaming. It keeps the Salesforce backend model: SCAPI, the Shopper Login and API Access Service (SLAS), and Managed Runtime.

Its HTML decision is explicit: [Storefront Next does not cache HTML documents](https://developer.salesforce.com/docs/commerce/sfra/guide/sfnext-caching-content.html) to support personalization.

For the same PDP, the initial request can read shopper state from HttpOnly cookies, fetch route data on the server, and stream personalized content. Storefront Next keeps tokens on the server and gives client components only a non-sensitive session projection.

When the returning shopper opens the page, Storefront Next can use their server-side session to include the relevant promotion and other personalized information in the rendered response. Less of the initial experience has to wait for browser-side fetching. The tradeoff is that the HTML document is produced for each request, making server and downstream latency part of the response path.

That server-first model affects more than the initial response. An initial page load or full-page navigation requests HTML from the server. After hydration, React Router client navigations request loader data and missing assets instead of another HTML document.

Without an HTML document cache, the performance strategy shifts to:

- Static-asset caching in the browser and eCDN.
  
- SCAPI web-tier and CDN caching where eligible.
  
- Parallel server-side data orchestration.
  
- Streaming non-critical data behind Suspense.
  
- Route-level code splitting.
  
- Resource hints, font optimization, deferred overlays, and idle rendering.
  

The shopper may receive a more complete initial page, but performance depends on efficient server orchestration and SCAPI caching. Slow downstream requests can be streamed later, but each one still needs an intentional loading state and a failure path.

### Next.js offers the best of both worlds

A Next.js storefront on Vercel keeps Salesforce as the commerce backend while replacing the Salesforce frontend template and runtime.

With Next.js 16 [Cache Components](https://nextjs.org/docs/app/getting-started/caching), a single route can contain:

- A cached, prerendered shell for stable product content.
  
- Request-time regions for shopper state, availability, or other runtime data.
  
- Client components for interaction-heavy behavior.
  

A simplified page can make that split explicit:

```tsx
import { Suspense } from "react";
import { ProductShell } from "@/components/product-shell";
import { ShopperOffer } from "@/components/shopper-offer";

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;

  return (
    <ProductShell productId={id}>
      <Suspense fallback={<div aria-busy="true">Loading your offer…</div>}>
        <ShopperOffer productId={id} />
      </Suspense>
    </ProductShell>
  );
}
```

The cached and dynamic behavior lives inside those components. Suspense alone does not make the shell cacheable. Cached functions or components use `'use cache'`, `cacheLife`, and `cacheTag`. Runtime components read request data such as cookies outside the cached scope.

In the same scenario, the product description and imagery can remain in the cached shell while the promotional price, availability, recommendations, and cart state resolve at request time. The shopper receives useful product content without waiting for every personalized field, and the team can decide which loading states are acceptable for each region.

Next.js also supports [time-based and on-demand revalidation](https://nextjs.org/docs/app/getting-started/revalidating):

- `cacheLife` defines a time policy inside a cached scope.
  
- `revalidateTag(tag, 'max')` marks tagged data stale and refreshes it with stale-while-revalidate behavior.
  
- `updateTag` expires a tag immediately from a Server Action for read-your-own-writes behavior.
  
- `revalidatePath` invalidates cached data associated with a route path.
  

To revalidate data, you can set up a webhook handler so that when a merchandiser changes a product or Page Designer page, a trusted event triggers the HTTP request that calls the relevant revalidation API. When the merchandiser schedules the promotion, the integration must translate that change into the appropriate cache revalidation event. Next.js provides caching controls, but the storefront team still owns the mapping between Salesforce events, cache tags, and the content shown to shoppers.

These differences extend beyond rendering: they also change deployment, integration work, and operational ownership.

## Side-by-side comparison

| Decision area           | PWA Kit                                                                                                               | Storefront Next                                                                      | Next.js on Vercel                                                               |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Initial rendering       | Express-oriented SSR with React Query hydration                                                                       | React Router loader-based SSR and streaming                                          | React Server Components, Suspense, Cache Components                             |
| HTML caching            | Page responses cacheable; 600-second default                                                                          | HTML documents not cached                                                            | Cached shell and request-time regions coexist                                   |
| Personalization         | Usually completed in the browser to keep HTML shareable; server cookie patterns are possible with safe cache behavior | Server-only auth and personalized SSR via HttpOnly cookies                           | Application-defined server session and dynamic regions                          |
| Data orchestration      | `commerce-sdk-react` hooks, server/client data flow                                                                   | Server loaders and actions, with MRT as the orchestration layer                      | Server Components, server functions, Server Actions, Route Handlers             |
| SCAPI behavior          | Same limits and cache rules                                                                                           | Same limits and cache rules                                                          | Same limits and cache rules                                                     |
| Runtime geography       | Edge services plus a selected MRT app-server region                                                                   | Edge services plus a selected MRT app-server region                                  | Global cached delivery plus configurable, region-first Functions                |
| Releases                | Immutable bundles deployed to MRT environments                                                                        | Immutable bundles deployed to MRT environments                                       | Preview Deployment per change, Production Deployment from the production branch |
| Salesforce integrations | PWA Kit implementations and SDK patterns                                                                              | Built-in analytics adapters, Business Manager setup, Storybook, template conventions | Rebuild or replace template-level integrations in the custom head               |
| Ownership               | Salesforce kit plus merchant customization                                                                            | Salesforce template plus merchant customization                                      | Customer owns frontend architecture and operations                              |

No option leads in every category. The choice depends on which constraints the team is trying to remove and which responsibilities it is willing to assume.

## Make the decision

### Stay on the PWA Kit

Staying on the PWA Kit can be the correct decision when:

- The current storefront meets business needs and can reach its Core Web Vitals targets through application-level improvements.
  
- A shareable SSR foundation fits the traffic and personalization model.
  
- The team can correct client bundle, data-fetching, session, and SCAPI issues without changing platforms.
  
- The migration risk exceeds the measured runtime constraint.
  
- Salesforce template continuity is more valuable than adopting a different frontend model now.
  

This is an optimization decision for an existing storefront, not the preferred starting point for a new architecture. Do not migrate because a newer option exists. Migrate because the current architecture prevents a requirement that matters enough to fund the move.

### Choose Storefront Next for template integration

Storefront Next fits when:

- The team wants Salesforce's Storefront Next template and its Managed Runtime operating model.
  
- Server-rendered personalization matters more than caching HTML documents.
  
- Business Manager provisioning, Storybook integration, and built-in Einstein, Active Data, and Data 360 analytics adapters reduce meaningful delivery work.
  
- React Router 7, Vite, Tailwind, TypeScript, and server loaders/actions fit the engineering model.
  
- The MRT deployment, regional execution, and support model meet operational requirements.
  

Storefront Next is a substantial architectural change from PWA Kit. Its trade-off is deliberate: the team receives Salesforce's template conventions and integrations while accepting Managed Runtime and a no-document-cache rendering model. Choose it when that exchange is more valuable than the additional caching, deployment, and framework control of a custom head.

### Choose Next.js on Vercel for a future-proof architecture

Next.js on Vercel is the recommended choice when the storefront is new or already being replatformed. It is strongest when:

- The storefront needs a cached shell with request-time regions in the same route.
  
- On-demand cache invalidation is tied to a reliable Salesforce or content event.
  
- Per-change previews and Vercel's release workflow solve a material problem for the team.
  
- The organization wants direct control over the frontend framework and upgrade schedule.
  
- The team is prepared to own the SLAS/session boundary, Page Designer renderer, analytics adapters, observability, security, and integration support.
  

For a Next.js storefront, Vercel reduces the platform work the team has to own. [Preview Deployments](https://vercel.com/docs/deployments/environments) make changes reviewable before release. Its [CDN](https://vercel.com/docs/cdn) serves cacheable output close to shoppers, reducing the need for repeated SCAPI work. [Observability](https://vercel.com/docs/observability) helps separate CDN, Function, browser, and Salesforce API bottlenecks, and the [Vercel Firewall](https://vercel.com/docs/vercel-firewall) adds DDoS mitigation and configurable Web Application Firewall (WAF) rules.

The advantage is visible in production storefronts. [Sonos](https://vercel.com/customers/how-sonos-amplified-their-devex) retained Salesforce Commerce Cloud while moving its frontend to Next.js on Vercel. Its engineering team described the architectural benefit directly:

[ASICS](https://vercel.com/partners/salesforce-commerce-cloud) made a similar move toward a composable Salesforce Commerce Cloud storefront:

These examples show what a custom frontend can enable, but they do not make it the right choice for every team. Use the following sequence to identify the best fit:

1. **Choose Next.js on Vercel** for a new storefront or planned replatform when the team can own the custom head. Its mixed cache/runtime model and delivery controls provide the most room for differentiated commerce experiences.
   
2. **Keep PWA Kit** when the storefront is already in production and measured improvements can meet the experience and operating goals without a migration.
   
3. **Choose Storefront Next** when Salesforce’s template integrations and Managed Runtime are more valuable to the organization than cacheable HTML and custom delivery control. Because migration from PWA Kit is a substantial undertaking, compare its total effort with the cost of building and operating a custom frontend.
   

## Next steps

- Plan a phased move with this [Salesforce incremental migration guide](https://vercel.com/blog/salesforce-incremental-migration).
  
- Explore the business case with this [composable commerce guide](https://vercel.com/blog/the-no-nonsense-guide-to-composable-commerce/).
  
- Learn [how requests move through Vercel’s CDN](https://vercel.com/docs/how-vercel-cdn-works).
  
- Reduce latency by [configuring Vercel Function regions](https://vercel.com/docs/functions/configuring-functions/region).
  
- Prepare for launch with the [production readiness checklist](https://vercel.com/docs/production-checklist).
  
- Prepare for peak traffic with the [Black Friday storefront guide](https://vercel.com/kb/guide/black-friday-preparation).
  
- See a [Salesforce Commerce Cloud migration in practice](https://vercel.com/customers/retailer-sees-10m-increase-in-sales-on-vercel).