skip to content

A Next.js App Router page exports `export const revalidate = 3600`. Does Next.js rebuild that page once an hour on a schedule? Explain what actually triggers the rebuild.

level: juniorimportance: must knowfreq 62%

answer

  1. a window, not a schedule
  2. nothing fires on its own
  3. a request is what wakes it up
  4. no traffic means no regeneration
  5. worst-case staleness exceeds the number

basics

~20 s

No scheduler is involved. The number is a staleness window in seconds: after 3600 seconds the cached page is merely marked stale, and only the next incoming request for that route causes Next.js to regenerate it.

solid answer

~50 s

`export const revalidate = 3600` does not create a cron job. At build time (or on first render) Next.js stores the rendered payload for that route with a timestamp. Every incoming request compares the entry's age against the window: inside the window it is served straight from the cache with no work at all; past the window the entry is treated as stale and that request is what triggers regeneration. So revalidation is **lazy and request-driven**. A route nobody visits for a week is not rebuilt 168 times — it is rebuilt zero times, and the first visitor after that week is the one who kicks the render off. The practical consequence is that the window is a *lower* bound on freshness, not a promise: worst-case staleness is the window plus the gap until the next request.

code

typescript · 13 lines
typescript
// app/products/page.tsx
// Staleness window, in seconds. Not a schedule.
export const revalidate = 3600

async function getProducts() {
  const res = await fetch('https://api.example.com/products')
  return res.json()
}

export default async function Page() {
  const products = await getProducts()
  return products.map((p: { id: string }) => p.id).join(', ')
}

go deeper

for a junior

Be ready to say the number is a staleness window measured in seconds and that a request, not a timer, causes the rebuild. Naming the unit and rejecting the cron idea is most of the answer at this level.

for a middle

Explain the request-time age check: fresh entries are served untouched, expired entries trigger a regeneration whose result replaces the stored copy. Mention that regeneration cost is bounded by traffic, not by the window.

for a senior

Show that you reason about worst-case staleness — window plus the gap to the next request — and about what happens when regeneration fails against a flaky upstream. Explain when a window alone is not an acceptable freshness guarantee.

for a principal

Frame the number as a staleness budget negotiated with the business, not a technical default. Own the tradeoff between regeneration cost on hot routes and freshness guarantees, and when to pair windows with event-driven invalidation.

## What the export actually declares In the App Router, a `page.tsx`, `layout.tsx` or `route.ts` file can export a route-segment configuration constant: ```ts // app/products/page.tsx export const revalidate = 3600 export default async function Page() { const products = await getProducts() return <ProductList products={products} /> } ``` The value is a number of **seconds**. It configures how long the cached output for that segment is considered fresh. It does not register a timer, a job, or a background worker anywhere. Nothing in the running server wakes up at the 3600-second mark. ## The lazy, request-driven cycle When the route is first rendered — usually during `next build` for a statically prerenderable route, otherwise on the first request — Next.js stores the rendered result in its server-side cache together with the moment it was produced. From then on, every request for that route runs the same check: 1. Compute the entry's age. 2. If age is **less than** `revalidate`, serve the stored entry. No component function runs, no data source is touched. 3. If age is **greater than or equal to** `revalidate`, the entry is stale. Next.js serves the request and schedules a regeneration of that route; when the new render finishes successfully it replaces the stored entry and the clock restarts. The important word is *schedules on a request*. The expiry is a condition evaluated at request time, not an event that fires on its own. ## No traffic, no work This is the half candidates most often get wrong, and it is the half that determines cost. Consider a site with 50,000 product pages, each with `revalidate = 3600`. A cron-style reading of the config predicts 50,000 renders per hour forever, which would be ruinous. The real behaviour is that regeneration is bounded by **traffic**: at most one regeneration per route per window, and only for routes somebody actually asked for. Pages in the long tail sit untouched in the cache for as long as nobody visits them, and cost nothing. The flip side is that freshness is not guaranteed by the number either. With `revalidate = 60` on a page that receives one visit per day, the data on that page can be nearly 24 hours old — the window expired long ago, but no request came to act on it. ## What resets and what does not A **successful** regeneration replaces the entry and resets its age. A regeneration that throws does not poison the cache: the previously stored good copy stays in place and keeps being served, and the next request tries again. That is deliberate — a flaky upstream API degrades you to stale content, not to an error page. A new deployment is a separate matter from revalidation: a build produces fresh output for the routes it prerenders, so shipping code is its own way of refreshing content, and that is exactly why teams sometimes redeploy to "fix" stale pages instead of wiring up invalidation. ## Choosing the number Because the window is a staleness budget rather than a schedule, pick it from the question "how out-of-date may this be before somebody is annoyed or misled?" — not from "how often does the data change?". Pricing and inventory tolerate seconds; an about page tolerates a day. Lowering the number does not make content update sooner in the absence of traffic; it only shortens the window that busy routes wait through, at the cost of more regenerations on those busy routes. When a change must be visible *now* rather than eventually, a time window is the wrong tool on its own: that is what the on-demand invalidation APIs `revalidatePath` and `revalidateTag` from `next/cache` exist for, typically triggered by the system that owns the content. A common production setup uses both — on-demand invalidation as the fast path, and a generous time window as a safety net for events that get lost. ## How to talk about it in an interview Say plainly: "It's a staleness window, evaluated on request. No cron. Cost scales with traffic, and worst-case staleness is the window plus the gap to the next request." That one sentence covers the mechanism, the cost model, and the limitation.

  • If regeneration is triggered by a request, what happens when the upstream API is down at that moment?
    The regeneration fails and the previously cached good copy stays in place — Next.js does not replace a working entry with an error. Visitors keep seeing stale content, and the next request after the failure tries again. That is usually the behaviour you want, but it means a silently broken upstream shows up as content that quietly stops updating rather than as errors.
  • The window has passed and content must be live immediately. What do you reach for instead?
    On-demand invalidation: `revalidatePath` or `revalidateTag` from `next/cache`, called from a Route Handler that the content system hits when something publishes. That marks the cached entries invalid at the moment of the change instead of waiting for a window to elapse. Most teams keep a time-based window as well, as a backstop for invalidation events that never arrive.
  • Is a shorter revalidate value always safer?
    No. It shortens the window that trafficked routes wait through, but it does nothing for untrafficked routes, and it multiplies regenerations on hot ones — each of which re-runs your data fetching. Very small values approach re-rendering on every request, which is a different rendering decision with a very different cost profile, made deliberately rather than by shrinking a number.

saying these in an interview costs you the question

  • Claims Next.js runs a background timer or cron per route
  • Says every page rebuilds on the window regardless of traffic
  • Believes the window guarantees content is never older than that
  • Thinks a failed regeneration wipes the cached page
  • Confuses the window with re-rendering on every request

context