A Next.js App Router Server Component calls `fetch(url, { next: { revalidate: 60 } })`. What exactly does that 60 apply to?
answer
- one number, one entry
- not a page-level setting
- also the opt-in, not just a TTL
- stale-then-refresh, not delete-on-expiry
basics
~20 sThe 60 seconds belongs to that one Data Cache entry — the one keyed from that URL and those options — not to the component, the page, or any other fetch. Supplying it also opts the fetch into caching.
solid answer
~50 sIt is a lifetime on **that single Data Cache entry**, the one keyed from that URL plus the options passed with it. It is not a property of the component, the page, or the layout, and a second fetch three lines below with `revalidate: 3600` gets its own independent lifetime. Supplying `next.revalidate` is also an opt-in: it puts a fetch into the Data Cache that, under Next 15 and 16 defaults, would otherwise not be cached at all. After 60 seconds the entry is *stale*, not deleted — Next can still answer from it while it refreshes the data behind the scenes, so the first visitor past the boundary usually sees the old value and the next one sees the new. One caveat worth naming: in a statically rendered route, a fetch whose `revalidate` is lower than the segment's own can pull the whole route's regeneration interval down to match.
go deeper
Know that the number is a number of seconds, that it is attached to that one request's stored result, and that passing it is what asks Next to store the result in the first place.
Be ready to explain that the entry is keyed from URL plus options, that expiry marks it stale rather than deleting it, and that two fetches in one file carry two independent lifetimes.
Show that you can diagnose the classic reports — 'it updated on the second load' and 'the page regenerates far more often than we configured' — by reasoning about stale-then-refresh and about the shortest interval in the route.
Own how intervals are chosen across a codebase: derive them from how often each source actually changes, and decide where a fixed interval should give way to explicit invalidation so freshness is not a number each author guesses independently.
## The unit is the cache entry, not the page The most common mental-model error here is thinking `next.revalidate` configures the page. It does not. Next's server `fetch` wrapper writes each cached result into the **Data Cache** under a key derived from the request URL and the options you passed — method, headers, body. `next: { revalidate: 60 }` stamps a lifetime onto *that* entry. ```ts export async function loadDashboard() { // entry A: lifetime 60s const prices = await fetch('https://api.example.com/prices', { next: { revalidate: 60 }, }) // entry B: lifetime one hour, completely independent of entry A const copy = await fetch('https://api.example.com/marketing-copy', { next: { revalidate: 3600 }, }) return { prices: await prices.json(), copy: await copy.json() } } ``` Two fetches, two entries, two clocks. Nothing about entry A's staleness touches entry B. ## It is also an opt-in Under the Next 15 and 16 defaults a bare `fetch` in a Server Component is not cached. There are two ways to opt a fetch into the Data Cache: `cache: 'force-cache'`, or supplying `next: { revalidate: N }`. So `revalidate` is doing double duty — it says *cache this* and *for this long*. Reading it as "a TTL applied to something already being cached" is only true on the older Next 14 default where everything was cached to begin with. The complementary value is `next: { revalidate: false }`, which asks for an entry with no time-based expiry — the same shape as `force-cache`, cached until something invalidates it explicitly. ## Stale, not evicted After the interval elapses the entry is not thrown away. It is marked stale. The typical behaviour is that a request arriving after the boundary can still be answered from the stored value while Next fetches fresh data in the background and replaces the entry; the *next* request then gets the new value. That is why teams testing a 60-second interval often report "it took two page loads to update" — the first load past the boundary is the one that triggers the refresh, not the one that benefits from it. The practical reading of `revalidate: 60` is therefore "this data may be up to about a minute old, occasionally a little more," not "this data is guaranteed fresh within 60 seconds." ## Interaction with the route's own interval A route segment can declare its own revalidation interval. When a fetch inside that route asks for a *shorter* interval than the segment's, the shorter one wins for the route as a whole — a fetch can pull the route's regeneration cadence down, and this is a frequent surprise: one aggressive `revalidate: 10` on a minor widget makes an otherwise hourly page regenerate every ten seconds. It is worth grepping for the smallest value in a route before blaming infrastructure for regeneration churn. ## Choosing the number The interval is a statement about tolerable staleness, and it is cheap to get wrong in both directions: - **Too short** and you have effectively bought uncached behaviour with extra machinery: the entry is stale nearly every time it is read, so the origin is hit at close to full rate anyway. - **Too long** and you have a page nobody can correct without a deploy, which is the situation that makes teams reach for on-demand invalidation instead. A reasonable default is to pick the interval from how often the *source* actually changes, not from how fresh you wish the page felt. Content edited a few times a day does not need a ten-second interval; a price feed updated continuously is not well served by a fixed interval at all and is a candidate for either an uncached fetch or explicit invalidation when the source changes. ## Things that catch people out - `next.revalidate` is a Next extension, not part of the Web `fetch` standard. It is meaningful only where Next's server wrapper is in play. - Combining `cache: 'no-store'` with `next: { revalidate: 60 }` on one call is self-contradictory — one says never store, the other says store for a minute. Pick one. - Changing the number does not retroactively re-stamp entries already sitting in the cache from an earlier deploy; the new lifetime applies to entries written afterwards. - Because the entry is keyed from URL plus options, adding a header changes the key. Two variants of the "same" request each carry their own clock.
- A team sets `revalidate: 60` and reports that the page still shows old data 90 seconds after the source changed. Is that a bug?Usually not. Passing the boundary makes the entry stale; the request that finds it stale is typically served the old value while the refresh happens behind it, so the update lands on a subsequent request. If nobody visits for a while, nothing refreshes at all — the clock does not run a background job on an idle page. Treat the number as an upper bound on how fresh the data can be, not a guarantee.
- What does `next: { revalidate: false }` ask for, and how does it differ from `cache: 'force-cache'`?Both produce a Data Cache entry with no time-based expiry, cached until something invalidates it explicitly. `false` states that intent through the same option you use elsewhere for numbers, which reads more consistently in a file where sibling fetches carry intervals. Functionally they are asking for the same thing: store it, and do not expire it on a clock.
- Why can one fetch's interval change how often the whole route is regenerated?A statically rendered route has its own revalidation cadence, and the framework reconciles it with the fetches inside it by taking the shortest interval. So a widget asking for ten seconds drags an hourly page down to ten seconds. When a page regenerates far more often than expected, the first thing to look for is the smallest `revalidate` anywhere in that route's tree.
saying these in an interview costs you the question
- Thinking revalidate on a fetch configures the whole page
- Assuming the entry is deleted the instant the interval passes
- Expecting a background timer to refresh an unvisited page
- Combining no-store with a revalidate number on one call
- Believing revalidate is a standard Web fetch option