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 

AspectPWA KitStorefront Next
Data LocationInside components (hooks)Outside components (loaders)
DiscoveryAutomatic (hook scanning)Explicit (route exports)

Data Fetching 

FeaturePWA KitStorefront Next
SSR ApproachReact Query auto-discoveryLoader functions
Client NavigationReact Query refetchClient loader
Legacy SupportgetProps static methodN/A
CachingReact Query cacheManual / React Router

Request Processing 

FeaturePWA KitStorefront Next
Pre-fetch ProcessingExpress middlewareReact Router middleware
Shared StateExpress res.localsRouter context
Request AccessFull Express req/resFetch API Request

Loading States and Suspense 

AspectPWA KitStorefront Next
ApproachManual conditional renderingDeclarative Suspense boundaries
Loading detectionisLoading flags from hooksuse() hook suspends on pending promises
Skeleton displayExplicit if (isLoading) checksAutomatic via Suspense fallback
Multiple regionsManual state tracking per regionMultiple <Suspense> boundaries
Navigation transitionskeepPreviousData optiongetPageKey + shouldRevalidate
Code splitting@loadable/component fallbackReact Router automatic splitting
Skeleton stylingChakra <Skeleton> componentTailwind animate-pulse
StreamingLimitedNative promise streaming

Pattern Comparison 

PWA Kit — imperative:

1function ProductList() {
2  const { data: products, isLoading } = useProducts();
3
4  if (isLoading) {
5    return <ProductListSkeleton />;
6  }
7
8  return products.map((p) => <ProductTile product={p} />);
9}

Storefront Next — declarative:

1function ProductList({ loaderData }) {
2  const products = use(loaderData.products);
3  return products.map((p) => <ProductTile product={p} />);
4}
5
6export default createPage({
7  component: ProductList,
8  fallback: <ProductListSkeleton />,
9});

Streaming Vs Waterfall 

PWA Kit can experience client-side waterfalls when data-fetching hooks depend on each other:

1// Sequential fetching
2const { data: product } = useProduct({ id: productId });
3const { data: category } = useCategory({ id: product?.categoryId }); // Waits for product

Storefront Next enables streaming where data resolves as it’s ready:

1// Loader returns promises
2export function loader({ params, context }) {
3  const productPromise = fetchProduct(params.productId);
4  const categoryPromise = productPromise.then((p) => fetchCategory(p.categoryId));
5
6  return {
7    product: productPromise,
8    category: categoryPromise, // Streams after product
9  };
10}

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.
  • keepPreviousDatashouldRevalidate: 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.