Lesson 9 of 81
Older Next.js cached every fetch() call by default and made you opt out. This version inverts that completely — fetch() is not cached by default, and you have to opt in — which quietly breaks a lot of muscle memory.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
You set both cache: 'no-store' and next: { revalidate: 60 } on the same fetch() call. What happens?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Next.js extends the standard Web fetch() API with a cache option that controls how a server-side fetch interacts with Next.js's own persistent cache, on top of whatever the URL's HTTP headers say. Four things control that behavior:
cache: 'auto no cache' — the actual default. Next.js refetches from the network on every request in development, and only fetches once at build time if the route has no Request-time APIs (so it can be statically prerendered); otherwise it refetches on every request in production too.cache: 'force-cache' — opt in explicitly to the persistent cache. A matching request (same URL, method, headers, body) is served from cache if fresh.cache: 'no-store' — always refetch from the network, every request, regardless of whether the route has Request-time APIs.next: { revalidate: <seconds> } and next: { tags: [...] } — set a cache lifetime and cache tags on a request, independent of the cache option.// app/blog/page.tsx
export default async function BlogPage() {
// Default: refetched every request in dev; cached once at build
// only if this route has no cookies()/headers()/searchParams usage.
const latest = await fetch("https://api.example.com/posts?sort=latest");
// Explicit opt-in to the persistent cache, revalidated hourly,
// taggable for on-demand invalidation.
const featured = await fetch("https://api.example.com/posts/featured", {
cache: "force-cache",
next: { revalidate: 3600, tags: ["featured-posts"] },
});
// Always fresh, no exceptions — used for something like a live counter.
const liveCount = await fetch("https://api.example.com/posts/count", {
cache: "no-store",
});
const [posts, feature, count] = await Promise.all([
latest.json(),
featured.json(),
liveCount.json(),
]);
return <BlogView posts={posts} feature={feature} count={count} />;
}
Each fetch() call here makes its own independent decision — one route can mix all three caching behaviors freely. This is separate from request memoization: if the exact same fetch() (same URL and options, GET method) is called more than once during a single render pass — once in a layout, once in a page, once in a nested component — React deduplicates those calls automatically and executes the network request only once, sharing the result. Memoization lasts only for that one render pass; it isn't a persistent cache and doesn't survive to the next request the way force-cache does.
auto no cache means "refetch every request in dev, and in production refetch every request too unless the route has zero Request-time APIs, in which case fetch once at build." It is never "cached forever by default" the way it used to be.force-cache and next.revalidate answer different questions. cache: 'force-cache' decides whether a request can be served from cache at all; next.revalidate decides how long a cached entry stays fresh once it's there. You typically set both together.fetch cache are two unrelated mechanisms. Memoization dedupes identical calls within one render pass and has nothing to do with cache options; the persistent cache spans requests and is controlled entirely by cache/next.revalidate.revalidate number and cache: 'no-store' on the same call is a contradiction Next.js rejects — both are ignored and a dev-mode warning is printed, rather than one silently winning."Why is this page hitting our API on every single request when it used to be cached automatically?" is a real question people hit migrating from older Next.js — the correct answer is explaining the inversion of the default, not just naming force-cache as a fix.