Fresh archive: Caching Data Loaders
Cache your data loaders with keys and TTL to reduce latency and API load.
Retired Fresh / Deno reference. This original guide preserves the earlier runtime APIs, Preact/HTMX examples, and any former admin steps. Use it when maintaining that runtime; consult the Studio Site Editor guides for current editing. Migration notes distinguish editor and framework migration.
Why cache loaders?
Loaders fetch data that powers your pages and sections. Caching them:
- Reduces API latency and backend load
- Improves resilience under traffic spikes
- Makes async rendering faster by serving warm data
Strong recommendation: enable caching for all read-mostly and public data loaders. Opt out only for user-specific or highly volatile data.
How loader caching works
In a loader module, export two optional fields:
// Cache policy
export const cache =
// "no-store" | "no-cache" | "stale-while-revalidate" | { maxAge: number }
// Cache key generator
export const cacheKey = (props, req, ctx) => string | nullCache modes
-
"no-store"(default):- Disables cache entirely for this loader.
- Also prevents dependent sections from being cached at the CDN edge.
- Use for user-specific or sensitive data (e.g. carts, sessions).
-
"no-cache":- Skips the cache for this loader run, but does not block dependent sections from being cached.
- Use when the loader must always run fresh but the section can still be served from cache.
-
"stale-while-revalidate":- Returns cached data immediately and revalidates in the background when stale.
- Best default for public, read-mostly loaders.
-
{ maxAge: number }:- Same as
"stale-while-revalidate"with a custom TTL (in seconds).
- Same as
TTL
The default TTL is 60 seconds, configurable via the CACHE_MAX_AGE_S environment variable or per-loader via export const cache = { maxAge: 300 }.
Cache key
The cacheKey(props, req, ctx) function must return a string that uniquely identifies the inputs that affect the response, or null to disable caching for that invocation.
The final cache key is composed of:
- The loader's resolver name
- The value returned by your
cacheKeyfunction
Good inputs to include in the key:
- Props that affect the result (slugs, filters, pagination)
- Request-scoped traits that change content (locale, currency, segment)
- Essential query params (but avoid tracking params or timestamps)
Examples:
// 1) Public, SWR with stable key
export const cache = "stale-while-revalidate";
export const cacheKey = (props: { slug: string }, req: Request) => {
const url = new URL(req.url);
url.search = new URLSearchParams([["slug", props.slug]]).toString();
return url.href;
};
// 2) Segment-aware key; bypass for logged-in users
export const cache = "stale-while-revalidate";
export const cacheKey = (_props: unknown, _req: Request, ctx: AppContext) => {
if (!ctx.isAnonymous) return null; // don't cache personalized data
return ctx.segment?.token ?? "anonymous";
};
// 3) Custom TTL (5 minutes)
export const cache = { maxAge: 300 };
export const cacheKey = (props: { category: string }, req: Request) => {
const url = new URL(req.url);
url.search = new URLSearchParams([["category", props.category]]).toString();
return url.href;
};
// 4) Explicitly opt-out (user-specific cart/session)
export const cache = "no-store";Cache invalidation
Loader caches are automatically invalidated on every new deployment — no manual action needed. Within a deployment, entries expire according to their TTL.
There is currently no way to manually invalidate a specific loader cache entry before the TTL expires.
Interaction with async rendering and CDN caches
- Loader cache reduces server-side latency and upstream API calls before the section is rendered.
- Stale Edge Cache (async render) caches the fully rendered section HTML at the CDN.
Use both together: fast loader data + CDN-cached sections = lowest possible latency end-to-end.
Best practices
- Prefer
"stale-while-revalidate"for public, read-mostly loaders. - Always implement
cacheKey— include all props and params that change the result, plus segmentation (locale, currency, segment) when relevant. - Return
nullfromcacheKeyfor authenticated or personalized responses. - Avoid including volatile or irrelevant parameters (timestamps, tracking params) in the key.
- If a loader must always execute fresh but you want the section cached, use
"no-cache"instead of"no-store".