Skip to main content

Caching

This document covers all caching strategies used in the Hydrogen storefront.

Cache Levelsโ€‹

The application defines 6 cache levels in app/lib/cache.ts:

LevelmaxAgestaleWhileRevalidateUse Case
faster1s9sNear real-time content (recommendation trays)
fast1 min4 minFrequently changing content (collections)
medium1 min14 minModerate freshness (PDP content, navigation)
slow5 min55 minPeriodically changing (footer, settings, feature flags)
slower15 min4 hrsLong-lived content (PLPs, Yotpo reviews, rollup maps)
none--No caching (account pages, authenticated data)

All cacheable levels use public cache mode except none which uses no-store.

Storefront API Cachingโ€‹

The Shopify Storefront API client is configured with Cloudflare Worker caching in app/lib/context.ts:

const hydrogenCacheKey = `birdygrey-${DEPLOY_RUN_ID}`;
const cache = await caches.open(hydrogenCacheKey);

Key points:

  • Each deployment gets an isolated cache namespace via DEPLOY_RUN_ID
  • Uses Hydrogen's createWithCache() wrapper for HTTP caching
  • All GraphQL queries go through this cached client by default

Redirect Cachingโ€‹

URL redirects use Hydrogen's CacheLong() strategy, which implements a 1-day cache duration per Hydrogen's defaults:

// app/lib/redirect/helper.server.ts
const redirectResponse = await storefront.query(URL_REDIRECT_QUERY, {
variables,
cache: storefront.CacheLong(),
});

Product Pricing on PDPsโ€‹

Product pricing data on PDPs uses Hydrogen's default cache strategy (CacheShort()) - no explicit cache configuration is applied at the application layer. CacheShort() provides 1 second of freshness with 9 seconds of stale-while-revalidate.

DataCacheFreshStale Revalidate
Product pricing (priceRange, compareAtPriceRange)CacheShort (default)1s9s
Sibling product IDs (COLOR/FABRIC groups)slow5 min55 min

The product query fetches pricing via storefront.query() without an explicit cache option:

// app/lib/api/storefront/common.ts
return storefront.query<T>(query, { variables }); // Uses Hydrogen default

This means:

  • Price changes in Shopify will reflect on PDPs within seconds (1s fresh + 9s stale-while-revalidate)
  • During the stale-while-revalidate window, users may see slightly outdated prices while fresh data is fetched in the background
  • Each deployment invalidates the cache, so prices update immediately after deploy

Route Cachingโ€‹

Routes can append headers either via the headers export, or using the data wrapper in loaders and actions. To set a standardized cache control header using one of our predefined caching strategies, use the generateCacheControlHeader() function.

To preserve the same cache headers when both client side navigating and navigating on a full document load, use the routeHeaders helper export.

import { CacheStrategy, routeHeaders } from '~/lib/cache';
import { generateCacheControlHeader } from '@shopify/hydrogen';
import { data } from 'react-router';

export const headers = routeHeaders;

export const loader = async ({ context }: LoaderFunctionArgs) => {
const headers = new Headers();
headers.append('Cache-Control', generateCacheControlHeader(CacheStrategy.fast));
return data({ ... }, { headers });
};

Route Cache Levelsโ€‹

RouteCache LevelDuration
Collection pagesfast1 min
Product pages(default)Storefront API default
Account pagesnoneNo caching
Predictive searchdynamic60s with term, 1hr without

API Route Cachingโ€‹

Internal API routes use withCache.run() to cache the result of an entire async operation (as opposed to withCache.fetch() which caches a single HTTP response):

// app/routes/api.yotpoReviews.tsx
const reviews = await context.withCache.run(
{
cacheKey: request.url,
cacheStrategy: CacheStrategy.slower,
shouldCacheResult: (result) => Boolean(result),
},
() => getProductReviews(...)
);

API Cache Levelsโ€‹

API RouteCache LevelDuration
/api/yotpoReviewsslower15 min
/api/yotpoQuestionsslower15 min
/api/yotpoMediaReviewsslower15 min
/api/yotpoUgc.$albumslower15 min
/api/predictive-searchdynamic60s-1hr
/api/admin/siblings.$style(none)Cached upstream

External Service Cachingโ€‹

Yotpoโ€‹

Yotpo API calls use withCache.fetch() in app/lib/yotpo/yotpo.ts:

  • Reviews/Questions: slower (15 min)
  • Access Token: slow (5 min)

GrowthBook Feature Flagsโ€‹

Feature flag fetches use slow cache (5 min) in app/lib/growthbook/helper.server.ts:

const features = await withCache.fetch(url, options, {
cacheStrategy: CacheStrategy.slow,
shouldCacheResponse: (body) => body.status === 200,
});

Collection Rollup Mapsโ€‹

Rollup maps fetched from external service use slower cache (15 min) in app/lib/plp/loader.helper.server.ts.

Builder.io CMS Cachingโ€‹

For detailed Builder.io caching documentation, see the CMS Environment & Caching Guide.

Summaryโ€‹

Builder content caching varies by content type:

Content TypeCache LevelFreshStale Revalidate
Recommendation traysfaster1s9s
Homepage, announcements, headerfast/medium1 min4-14 min
PDP content, PLP navigationmedium1 min14 min
Footer, breadcrumbs, settingsslow5 min55 min
PLPs, empty cartslower15 min4 hrs

Cache Bustingโ€‹

Builder cache can be disabled via BUILDER_CACHEBUST environment variable:

  • 'true' or 'all': Disable cache for all models
  • Comma-separated list (e.g., 'page,pdp,plp'): Disable specific models
  • Builder preview mode always disables cache

Static Fallbacksโ€‹

Critical UI components have static JSON fallbacks bundled at build time (app/lib/builder/static.server.ts):

  • Announcements (Core/Suits)
  • Header (Core/Suits)
  • Footer (Core/Suits)
  • Settings

These provide immediate fallback if Builder API is unavailable.

Cache Invalidationโ€‹

Deploy-based Invalidationโ€‹

Each deployment creates a new cache namespace via DEPLOY_RUN_ID, effectively invalidating all cached content:

const hydrogenCacheKey = `birdygrey-${DEPLOY_RUN_ID}`;

Stale-While-Revalidateโ€‹

All cache levels (except none) use stale-while-revalidate, allowing:

  • Immediate response with potentially stale content
  • Background revalidation for fresh content
  • Graceful degradation during origin issues

Quick Referenceโ€‹

ContentCache LevelFreshStale
URL RedirectsCacheLong1 day-
Collection Pagesfast1 min4 min
PDP Contentmedium1 min14 min
PLPsslower15 min4 hrs
Yotpo Reviewsslower15 min4 hrs
Feature Flagsslow5 min55 min
Rec Traysfaster1s9s
Account Pagesnone--