# SFRA vs PWA Kit: Choosing Your SFCC Storefront Architecture

URL: https://www.ozanaltinbas.com/en/blog/sfra-vs-pwa-kit
Author: Ozan Altinbas (Ozan Altınbaş), SFCC Technical Architect
Published: 2026-11-12
Category: Architecture | Tags: SFCC, SFRA, PWA Kit, Headless Commerce, SCAPI

---

SFRA and PWA Kit are the two storefront architectures most Salesforce B2C Commerce teams have shipped since 2021, and the choice fixes where pages render, where code runs, which skills you need and how hard the next migration will be. SFRA renders HTML on the B2C Commerce application server from cartridges. PWA Kit is a React application on Managed Runtime that talks to the platform over SCAPI.

The SFRA-or-headless question also has a new answer in 2026: Salesforce's [storefront chooser](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/which-product.html) now marks **Storefront Next** as the recommended option for new storefronts and has no separate PWA Kit row. This guide compares SFRA and PWA Kit technically, shows where Storefront Next fits, and goes deep on hybrid routing and session sync. For the business case for headless, read [Headless Commerce Cloud: is it right for your business?](https://www.ozanaltinbas.com/en/blog/headless-commerce-cloud); for a platform overview, start with the [SFCC guide](https://www.ozanaltinbas.com/en/sfcc).

---

## The short answer

- **SFRA** fits a team strong in the Script API and ISML that wants a complete storefront quickly and relies on connector cartridges with their own storefront UI.
- **Headless** fits a team with React and Node.js skills that wants app-like navigation or several front ends on one back end, and can own a second deployable. For a **new** headless build, evaluate Storefront Next before PWA Kit.
- **Hybrid** fits an existing SFRA site moving page groups to headless one at a time, with a due date for finishing.

---

## SFRA, PWA Kit, Composable Storefront, Storefront Next: what the names mean

Search results mix these terms:

| Name | What it is | Where it runs | How it reads commerce data |
|---|---|---|---|
| SFRA (Storefront Reference Architecture) | Reference storefront delivered as cartridges: JavaScript controllers in an MVC pattern, ISML templates, Bootstrap 4 | B2C Commerce application servers | Script API (`dw.*`), in the same process |
| PWA Kit | React framework plus the Retail React App template | Managed Runtime | SCAPI through `@salesforce/commerce-sdk-react` hooks; SLAS for shopper login |
| Composable Storefront | Salesforce's product name for PWA Kit **and** Managed Runtime together | Managed Runtime | SCAPI (OCAPI can be called too, but it is deprecated as of April 2026) |
| Storefront Next | Server-first React storefront: React Router 7 framework mode, React 19, Tailwind CSS, shadcn/ui, Vite | Managed Runtime | Generated SCAPI clients called from route loaders and actions |

The [Composable Storefront overview](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/getting-started.html) describes two technologies: PWA Kit to build the storefront and Managed Runtime to host it. Both came from Mobify (acquired by Salesforce in 2020), hence the `/mobify` paths. The [SFRA overview](https://developer.salesforce.com/docs/commerce/sfra/guide/sfra-overview.html) dates PWA Kit's launch, as an alternative to SFRA, to 2021.

---

## How SFRA works

An SFRA request goes through the eCDN to a B2C Commerce application server. The cartridge path resolves the controller, which builds view data through models and the Script API, and an ISML template renders the HTML. Routing, data access and rendering share one process: there is no network hop between "storefront" and "commerce engine".

You customise by overlay: a custom cartridge extends a base controller with `server.append`, `server.prepend` or `server.replace` and never edits `app_storefront_base` (see the [cartridge development guide](https://www.ozanaltinbas.com/en/blog/sfcc-cartridge-development)).

Caching is declared per route. SFRA ships middleware in [`app_storefront_base/cartridge/scripts/middleware/cache.js`](https://salesforcecommercecloud.github.io/b2c-dev-doc/docs/current/sfrajsdoc/js/server/app_storefront_base_cartridge_scripts_middleware_cache.js.html): `applyDefaultCache` (24 hours), `applyInventorySensitiveCache` (30 minutes) and two promotion-sensitive variants that also mark the response as personalised.

```javascript
// app_custom/cartridge/controllers/Brand.js
'use strict';

var server = require('server');
var cache = require('*/cartridge/scripts/middleware/cache');

// Rendered on the B2C Commerce app server; applyDefaultCache sets a 24-hour page cache
server.get('Show', cache.applyDefaultCache, function (req, res, next) {
    var CatalogMgr = require('dw/catalog/CatalogMgr');
    var category = CatalogMgr.getCategory(req.querystring.cgid);

    res.render('brand/landing', {
        categoryName: category ? category.displayName : null
    });
    next();
});

module.exports = server.exports();
```

You get one codebase, one deployment target and the whole Script API; you pay with a full document request on every navigation.

---

## How PWA Kit works

PWA Kit projects usually start from the **Retail React App**, a server-side rendered React storefront with home, listing, product, cart and checkout pages already built. Since v3, template extensibility lets you override individual files in an `overrides/` directory instead of forking the app.

Per the [PWA Kit overview](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/pwa-kit-overview.html), the same source runs in two contexts. The first request is rendered by Managed Runtime's **App Server** (React, Node.js and Express); the browser then hydrates the page and later navigations happen on the client. Salesforce's [Storefront Next migration reference](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/sfnext-pwa-overview.html) spells out a consequence: PWA Kit is stateless on the server, so pages are server-rendered as a guest shopper would see them, and client-side state lives in TanStack Query.

Commerce data comes from SCAPI through `commerce-sdk-react` hooks, with SLAS issuing shopper tokens. If your integrations still run on OCAPI, read [SCAPI vs OCAPI](https://www.ozanaltinbas.com/en/blog/scapi-vs-ocapi) first.

```javascript
// overrides/app/pages/product-detail/index.jsx (excerpt)
import {useProduct} from '@salesforce/commerce-sdk-react'

// productId comes from the route params. The hook calls the SCAPI Shopper
// Products API; React Query caches the response and hydrates it on the
// client after server-side rendering.
const {data: product, isLoading} = useProduct({
    parameters: {
        id: productId,
        allImages: true,
        expand: ['availability', 'images', 'prices', 'variations']
    }
})
```

### PWA Kit on Managed Runtime: services and limits

[Managed Runtime](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/mrt-overview.html) puts a web application firewall, an API proxy, a CDN and edge functions (such as the request processor) in front of the App Server. Deployments are immutable **bundles**; rolling back means deploying an earlier one. Constraints to design around:

- App Server requests time out after 20 seconds in production and 16 seconds elsewhere (HTTP 502).
- Requests and responses are capped at 6 MB; bundles at 400 MB.
- Cookies are **off by default** and must be enabled per environment. Hybrid Auth needs them.
- The production environment limit starts at 0, so ask Support to raise it well before launch. Since 29 September 2026, only environments associated with a Staging or Production B2C Commerce instance can be marked as production.
- Further environments cost nothing extra, up to 100 per organisation.

The default `Cache-Control` lifetime for PWA Kit page responses is 600 seconds. Salesforce's [cache hit ratio guide](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/maximizing-your-cache-hit-ratio.html) says to keep personalised and fast-changing content (names, cart counts, prices, inventory, promotions) out of the server render so the CDN can reuse it, and to strip marketing parameters in `app/request-processor.js` so similar URLs share a cache entry.

---

## Where Storefront Next fits

Storefront Next is Salesforce's newer headless storefront and the "recommended option" in the storefront chooser. It keeps PWA Kit's back end (SCAPI, SLAS and Managed Runtime) and changes the front end:

- **Server-first data flow.** Route loaders fetch data before rendering, with streaming SSR and Suspense, instead of PWA Kit's fetch-on-render hooks.
- **Sessions in `httpOnly` cookies** managed on the server, where PWA Kit commonly keeps tokens in browser storage.
- **Tooling.** Vite, Vitest and strict TypeScript instead of Webpack and Babel; React Router 7 file-based routes instead of React Router 5; Tailwind and shadcn/ui instead of Chakra.

Salesforce provides a migration tool for SiteGenesis, SFRA and PWA Kit storefronts. PWA Kit is still documented, the Hybrid Auth guide targets v3, and the Managed Runtime [feature retirement list](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/references/about-pwa-kit-managed-runtime/feature-retirement.html) names no PWA Kit end date. But its policy only guarantees security patches for 24 months after each major version's general availability, and v3.0.0 shipped in June 2023: confirm v3's support horizon with Salesforce before planning years ahead. Most of this article applies to both headless options; the differences sit in the React layer.

---

## SFRA vs PWA Kit side by side

| Dimension | SFRA | PWA Kit |
|---|---|---|
| Rendering | Server-side ISML; full document on every navigation | Server-side render on first request, then client-side navigation |
| Data access | Script API in the same process | SCAPI over HTTPS, SLAS tokens |
| Deployment | Code versions on B2C Commerce instances | Immutable bundles on Managed Runtime, plus a cartridge for hooks and Custom APIs |
| Page caching | Per-route page cache set by middleware | Managed Runtime CDN driven by `Cache-Control` (600 s default) |
| Personalisation | Rendered on the server per request; weigh it against the page cache | Server renders the guest view; personalise on the client |
| Core skills | Script API, ISML, cartridge overlays, Bootstrap | Universal React, Node.js and Express, REST APIs, Webpack ([Skills for Success](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/skills-for-success.html)) |
| Extending behaviour | `server.append`, `prepend`, `replace` in an overlay cartridge | Template extensibility, [SCAPI hooks](https://developer.salesforce.com/docs/commerce/commerce-api/guide/extensibility_via_hooks.html) (`before`, `after`, `modifyResponse`), [Custom APIs](https://developer.salesforce.com/docs/commerce/commerce-api/guide/custom-apis.html) |
| Page Designer | ISML page types and components; SFRA ships layouts and components | Headless Page Designer: React components, `arch_type: "headless"` metadata, `usePage()` |
| Einstein | Built-in display of Einstein recommendations and predictive search ([SFRA features](https://developer.salesforce.com/docs/commerce/sfra/guide/sfra-feature-list.html)) | Retail React App's `useEinstein` hook calls the Einstein API from the browser |
| Active Data | Native | PWA Kit 3.3.0 or later; product use cases only |
| Connector cartridges | Plug in, UI included | Server logic can run in hooks; storefront UI must be rebuilt in React |

**Page Designer.** [Headless Page Designer](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/page-designer.html) needs PWA Kit 2.7.0 or later, a SLAS client with the `sfcc.shopper-experience` scope, and `arch_type: "headless"` in every page, component and aspect type metadata file. Custom scripts in headless metadata are ignored, so logic that sat in a component script moves into React. Each Business Manager site can be connected to only one Managed Runtime environment. ISML and headless pages can coexist, so merchandisers keep working while pages migrate.

**Einstein.** The Retail React App's [`useEinstein` hook](https://github.com/SalesforceCommerceCloud/pwa-kit/blob/develop/packages/template-retail-react-app/app/hooks/use-einstein.js) sends activity events (product views, searches, add-to-cart, checkout steps) and fetches recommendations from the Einstein API, skipping both when Do Not Track is on. On a custom headless build you own that instrumentation, and recommendation quality depends on it.

---

## Performance: what actually differs

Neither architecture is fast by default; they fail in different places.

- **SFRA** pays for a full document request on every click. A page-cache hit is cheap; a miss runs controllers and templates on the app server. The usual problem is personalisation quietly lowering the cache hit rate (see the [SFCC caching guide](https://www.ozanaltinbas.com/en/blog/sfcc-caching)).
- **PWA Kit** pays on the first visit: server render, JavaScript download and hydration on the shopper's device. After that, Salesforce notes, "subsequent page requests can often be served without server requests". The client-side weight is the trade-off that Storefront Next's server-first model addresses.

Salesforce's [go-live checklist](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/pwa-best-practices.html) recommends Lighthouse CI from mid-implementation so regressions surface early. Ignore vendor benchmark tables: compare your own field data (Chrome UX Report or RUM) for the templates that carry revenue.

---

## SEO implications

Both send server-rendered HTML; for PWA Kit the question is what that HTML contains.

- **Check the server render.** Append `?__server_only` to a PWA Kit URL to stop hydration and see what the server delivered. Entry pages (home, category, PLP, PDP) need their real content there, with little layout shift once the client takes over.
- **Keep your URLs.** The Retail React App's default routes, `/category/:categoryId` and `/product/:productId`, are unlikely to match your SFRA URL rules. Salesforce's checklist notes most customers keep their existing URLs, and recommends redirecting any that won't exist and watching rankings in Search Console. Managed Runtime manages redirects, and SCAPI has a Shopper SEO `url-mapping` endpoint.
- **Sitemaps still come from the platform.** For PWA Kit, hybrid or custom headless sites, B2C Commerce can [generate the sitemap](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/create-a-sitemap.html) once an eCDN vanity domain and matching hostname alias exist; routes not configured in Business Manager go through the `uploadCustomSitemapAndTriggerSitemapGeneration` endpoint.
- **In hybrid, crawlers see one hostname**, but two stacks emit `<head>` tags, so canonical, hreflang and structured-data logic must produce identical output on both.

The [Commerce Cloud SEO guide](https://www.ozanaltinbas.com/en/blog/commerce-cloud-seo-guide) covers the platform-side settings.

---

## Cost to build and run

Licensing depends on your contract (see [Salesforce Commerce Cloud pricing](https://www.ozanaltinbas.com/en/blog/salesforce-commerce-cloud-pricing)), but the structural costs are clear:

- **SFRA:** one codebase, one pipeline, one skill set, and a complete reference storefront to start from.
- **PWA Kit:** two codebases (the React app, plus cartridges for hooks, Custom APIs, jobs and services), two pipelines, Managed Runtime roles in Account Manager, and front-end dependency upgrades you own.
- **Hybrid:** both, plus the routing between them. In Salesforce's words, "the longer your site is in hybrid mode, the more time you have to spend on the operating complexity involved".

---

## Hybrid storefronts: running SFRA and PWA Kit together

Salesforce calls this a **hybrid storefront** or **phased headless rollout**: one hostname, some paths served by SFRA, others by a headless storefront on Managed Runtime.

### How traffic is split

The eCDN, available on Primary Instance Group (production, staging, development) and on-demand sandbox instances, evaluates a Cloudflare rule expression at the edge: matching paths go to the Managed Runtime origin, everything else to SFRA. You set the rule in the Business Manager eCDN UI or with the CDN Zones API's `createMrtRules` endpoint ([CDN routing rules](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/ecdn-rules-for-phased-headless-rollout.html)).

The [hybrid routing matrix](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/hybrid-routing-matrix.html) lists six ready-made splits: Modern Core (only cart and checkout stay on SFRA), Modern Checkout, Modern Entry Only, Legacy Entry Only, Catalog-Only, and Account and Auth Only. The Catalog-Only expression sends category, product and search pages to PWA Kit:

```text
(http.request.uri.path matches "^(/[^/]+/[^/]+)?/(category|product|search)(/.*)?$" or http.request.uri.path matches "^/mobify.*" or http.request.uri.path matches "^/callback.*" or http.request.uri.path matches "^/worker\.js.*")
```

The last three clauses are required in every configuration: `/mobify` carries PWA Kit's proxies and static assets, `/callback` the SLAS authentication callback, and `/worker.js` the service worker.

Edge routing is half the job. In the PWA Kit app, remove SFRA-owned paths from `app/routes.jsx` (or `overrides/app/routes.jsx`) so React Router does a full page load instead of a client-side transition, and make the catch-all route redirect to the default origin instead of rendering `PageNotFound`. Keep the two definitions complementary. Localhost has no eCDN, so local development uses the hybrid proxy in `pwa-kit-runtime`.

### How sessions stay in sync: Hybrid Auth

SFRA identifies a shopper with the `dwsid` session cookie; PWA Kit uses a SLAS JWT. Since B2C Commerce 25.3, [Hybrid Auth](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/hybrid-auth.html) keeps the two in sync inside the platform and replaces the older Plugin SLAS cartridge. The setup in outline:

1. Create a SLAS client (public or private) with the `sfcc.session_bridge` scope.
2. In Business Manager, open **Merchant Tools > Site Preferences > Hybrid Auth Settings**, enable it and enter the same client ID.
3. Remove `plugin_slas` from the cartridge path, and make sure no custom code calls the session bridge endpoints itself.
4. In PWA Kit, enable `hybridAuthEnabled` on `CommerceApiProvider`, then enable cookies on the Managed Runtime environment.

```javascript
// overrides/app/components/_app-config/index.jsx (excerpt)
<CommerceApiProvider
    // ...existing props
    hybridAuthEnabled={true}
>
```

The setup guide's prerequisites are PWA Kit 3.12 or later and `commerce-sdk-react` 4.0.0 or later; new implementations must start on v3. Third-party identity provider logins must run through SLAS, not Business Manager. Hybrid Auth supports SiteGenesis as well as SFRA.

### Storefront Next in hybrid mode

The eCDN routing and Hybrid Auth setup are the same. On the app side, [Storefront Next](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/sfnext-hybrid.html) declares which paths belong to SFRA in `config.server.ts`:

```typescript
site: {
    hybrid: {
        enabled: true,
        legacyRoutes: ['/cart', '/checkout', '/checkout/:confirmationId'],
    },
}
```

The eCDN rule must also send `^/resource.*` and `^/action/.*` to Managed Runtime, because React Router resource routes and actions live there.

### Hybrid rules of thumb

- **Move more than one page.** Salesforce [recommends](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/hybrid-implementation.html) the home page plus the next one or two steps of the most common journey; the SPA benefit only appears when shoppers navigate between headless pages.
- **Read the basket guidance** in Salesforce's [hybrid implementation guidance](https://developer.salesforce.com/docs/commerce/commerce-api/guide/hybrid-storefront-baskets.html) before both stacks write to the same basket.
- **Set an end date.** Every month in hybrid means two front ends to patch and test.

---

## Migration paths

| From | To | Route |
|---|---|---|
| SFRA | PWA Kit or Storefront Next | Hybrid rollout by page group, starting with Catalog-Only or Modern Core |
| PWA Kit | Storefront Next | Salesforce's migration reference and migration tool; same SCAPI, SLAS and Managed Runtime back end |
| SiteGenesis | Headless | Hybrid with Hybrid Auth; Salesforce does not support SiteGenesis with Plugin SLAS |

Server-side assets survive a move to headless: services, jobs, custom objects and Business Manager configuration. The presentation layer does not: ISML, client-side JavaScript and view logic in SFRA controllers. Business logic in controllers must move into [SCAPI hooks](https://www.ozanaltinbas.com/en/blog/sfcc-hooks) or Custom APIs, and that refactor is often what an estimate misses.

---

## Decision matrix

| Situation | Choose | Why |
|---|---|---|
| New storefront, Script API and ISML team, fixed launch date | SFRA | Complete reference storefront, one deployable, connector cartridges plug in |
| New storefront, React and TypeScript team, long-term headless plan | Storefront Next, after evaluating it against PWA Kit | Salesforce's recommended option; same SCAPI, SLAS and Managed Runtime back end |
| Live PWA Kit site that performs well | Stay on PWA Kit v3, keep upgrading, plan the Storefront Next move | Still documented and targeted by Hybrid Auth; a migration reference and tool exist |
| Live SFRA site with slow browse pages | Hybrid, catalogue pages first | eCDN routing plus Hybrid Auth; checkout stays untouched |
| Many connector cartridges with storefront UI | SFRA, or hybrid with those flows left on SFRA | UI parts must be rebuilt in React |

---

## Key takeaways

- SFRA keeps everything in one process on the B2C Commerce app server; PWA Kit moves the storefront onto Managed Runtime and reaches commerce data over SCAPI and SLAS.
- In 2026 the headless choice is PWA Kit **or** Storefront Next. Salesforce recommends Storefront Next for new storefronts; PWA Kit is still documented, but confirm v3's support horizon.
- Hybrid is documented: eCDN rules split paths, `app/routes.jsx` filtering stops soft navigation into SFRA, and Hybrid Auth keeps `dwsid` and the SLAS token in sync.
- The headless SEO risk is the server render's content and the URL change, not JavaScript crawling as such.
- Page Designer, Einstein and connector cartridges all work headless, but each needs rebuild work. Estimate it before choosing.

For an architecture review of your storefront options or a phased headless plan, see the [SFCC consulting services](https://www.ozanaltinbas.com/en/service) or [get in touch](https://www.ozanaltinbas.com/en/contact).

## Further reading

- [Choose Your B2C Commerce Storefront Type](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/which-product.html): Salesforce's current storefront chooser, including Storefront Next.
- [Composable Storefront Overview](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/getting-started.html): what PWA Kit and Managed Runtime each do.
- [Hybrid Storefront Routing Matrix (PWA Kit)](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/hybrid-routing-matrix.html): the six route splits with their eCDN expressions.
- [Configure a Hybrid Storefront with Hybrid Auth (PWA Kit)](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/hybrid-auth.html): session sync setup, end to end.
- [Migrate from PWA Kit to Storefront Next](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/sfnext-pwa-overview.html): the architectural differences between the two headless options, area by area.
