---
schema_version: "1"
id: cf8f86a93017a000fe279e4c1099c49f58e93ef8180a26a717ebcf44e903a3fd
title: Data Fetching and Loading States Differences with PWA Kit
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/commerce-cloud/sfnext/guides/sfnext-pwa-data-fetching.md
canonical_url: https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-pwa-data-fetching.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: ba0c71af53bd885ffb5800a42895629dc68920181aded9a179ab6eb6a502a4ec
products:
  - Composable Storefront
  - Salesforce APIs
---
# Data Fetching and Loading States Differences with PWA Kit

PWA Kit follows a fetch-on-render model where hooks request data during component render and expose manual loading flags. Storefront Next follows fetch-then-render with route loaders that gather data before component rendering, enabling Suspense and streaming patterns more naturally. This moves data concerns out of presentation components and simplifies loading orchestration.

PWA Kit and Storefront Next handle loading states with different models. PWA Kit relies on manual conditional rendering, while Storefront Next uses React Suspense for declarative loading boundaries. PWA Kit’s approach gives explicit control through React Query’s `isLoading` flags and conditional rendering. Storefront Next’s Suspense-based approach keeps components focused on rendering with data while boundaries handle loading automatically. That reduces boilerplate and enables streaming, but it requires understanding Suspense patterns and React 19’s `use()` hook.

## Paradigm

| Aspect        | PWA Kit                   | Storefront Next              |
| ------------- | ------------------------- | ---------------------------- |
| Data Location | Inside components (hooks) | Outside components (loaders) |
| Discovery     | Automatic (hook scanning) | Explicit (route exports)     |

## Data Fetching

| Feature           | PWA Kit                    | Storefront Next       |
| ----------------- | -------------------------- | --------------------- |
| SSR Approach      | React Query auto-discovery | Loader functions      |
| Client Navigation | React Query refetch        | Client loader         |
| Legacy Support    | `getProps` static method   | N/A                   |
| Caching           | React Query cache          | Manual / React Router |

## Request Processing

| Feature              | PWA Kit                  | Storefront Next         |
| -------------------- | ------------------------ | ----------------------- |
| Pre-fetch Processing | Express middleware       | React Router middleware |
| Shared State         | Express `res.locals`     | Router context          |
| Request Access       | Full Express `req`/`res` | Fetch API `Request`     |

## Loading States and Suspense

| Aspect                 | PWA Kit                          | Storefront Next                           |
| ---------------------- | -------------------------------- | ----------------------------------------- |
| Approach               | Manual conditional rendering     | Declarative Suspense boundaries           |
| Loading detection      | `isLoading` flags from hooks     | `use()` hook suspends on pending promises |
| Skeleton display       | Explicit `if (isLoading)` checks | Automatic via Suspense fallback           |
| Multiple regions       | Manual state tracking per region | Multiple `<Suspense>` boundaries          |
| Navigation transitions | `keepPreviousData` option        | `getPageKey` + `shouldRevalidate`         |
| Code splitting         | `@loadable/component` fallback   | React Router automatic splitting          |
| Skeleton styling       | Chakra `<Skeleton>` component    | Tailwind `animate-pulse`                  |
| Streaming              | Limited                          | Native promise streaming                  |

### Pattern Comparison

PWA Kit — imperative:

```javascript
function ProductList() {
  const { data: products, isLoading } = useProducts();

  if (isLoading) {
    return <ProductListSkeleton />;
  }

  return products.map((p) => <ProductTile product={p} />);
}
```

Storefront Next — declarative:

```typescript
function ProductList({ loaderData }) {
  const products = use(loaderData.products);
  return products.map((p) => <ProductTile product={p} />);
}

export default createPage({
  component: ProductList,
  fallback: <ProductListSkeleton />,
});
```

### Streaming Vs Waterfall

PWA Kit can experience client-side waterfalls when data-fetching hooks depend on each other:

```javascript
// Sequential fetching
const { data: product } = useProduct({ id: productId });
const { data: category } = useCategory({ id: product?.categoryId }); // Waits for product
```

Storefront Next enables streaming where data resolves as it's ready:

```typescript
// Loader returns promises
export function loader({ params, context }) {
  const productPromise = fetchProduct(params.productId);
  const categoryPromise = productPromise.then((p) => fetchCategory(p.categoryId));

  return {
    product: productPromise,
    category: categoryPromise, // Streams after product
  };
}
```

## Key Similarities

PWA Kit and Storefront Next have these data fetching capabilities.

- Server-side rendering.
- Fetching data from any data source.
- Setting response headers.
- Middleware/pre-processing capabilities.

## Storefront Conversion Considerations

When converting your storefront from PWA Kit to Storefront Next, keep these tips in mind.

- Move data fetching to loaders: Extract `useQuery` calls into `loader` functions.
- Replace React Query: Use loader returns instead of hooks.
- Conditional rendering → Suspense: Replace `if (isLoading)` patterns with Suspense boundaries and the `createPage` factory.
- Update loading states: Replace `isLoading` conditionals with Suspense fallbacks.
- `keepPreviousData` → `shouldRevalidate`: Move “keep old data visible” logic from React Query options to router revalidation control.
- Loading spinners → skeleton fallbacks: Convert inline loading spinners to skeleton components in Suspense fallbacks.
- Multiple loading states → multiple boundaries: Split single-page loading into independent Suspense regions for granular streaming.
- Convert middleware: Express middleware becomes React Router middleware.
- Update request access: Use Fetch API `Request` instead of Express `req`.
