export const revalidate

Sets the default revalidation interval, in seconds, for a layout, page, or route handler.

Signature
export const revalidate = false
// false | 0 | number
  • Removed in PHP v16.0.0 (when cacheComponents is enabled)

Parameters

ParameterTypeDescription
$falseoptionalboolean literal= falseDefault. The default heuristic: cache fetch requests that set cache: 'force-cache' or are discovered before a Request-time API is used. Semantically equivalent to revalidate: Infinity — the resource is cached indefinitely.
$0optionalnumber literalEnsures the layout or page is always dynamically rendered, even when no Request-time API or uncached fetch is found. Changes the default of fetch requests with no cache option to 'no-store', but leaves fetches that opted into 'force-cache' or a positive revalidate as they are.
$numberoptionalnumberIn seconds. Sets the default revalidation frequency of the layout or page to n seconds.

Errors

The value must be statically analyzable — revalidate = 600 is valid, revalidate = 60 * 10 is rejected.

Changelog

  • PHP v16.0.0dynamic, dynamicParams, revalidate and fetchCache are removed when Cache Components (the cacheComponents flag) is enabled; time-based caching moves to 'use cache' with cacheLife.
  • PHP n/aThe revalidate value is not available when using the deprecated runtime = 'edge'.

revalidate sets the default revalidation window for everything rendered by a segment. It does not override the next.revalidate value an individual fetch() sets — those still win, and a lower one pulls the whole route down with it.

Two rules that decide the real number

  1. The lowest revalidate across every layout and page in a route determines the frequency of the entire route. A root layout at 60 means no page under it can be fresher than 60 seconds apart from its own lower value — and a leaf page at 10 drags the whole route to 10.
  2. An individual fetch() with a lower next: { revalidate } also lowers the route's frequency.

What trips people up

The value is read by the compiler, not evaluated. export const revalidate = 60 * 10 looks harmless and fails the build; write 600, or a plain identifier the bundler can fold. Importing the number from a shared constants file is the usual way this breaks.

In development, pages are always rendered on demand and never cached, so you cannot observe the interval with next dev — run next build && next start to see it. And revalidate is unavailable under the deprecated runtime = 'edge'.

Example

Example · typescript
export const revalidate = 3600

export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const res = await fetch(`https://api.example.com/posts/${slug}`)
  const post: { title: string; body: string } = await res.json()

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </article>
  )
}

When to use it

  • Regenerate a blog post page an hour after the last request without redeploying.
  • Refresh a product listing every 60 seconds while keeping it served from the cache.
  • Set revalidate = 0 on a layout to keep an authenticated area out of the static cache entirely.

Frequently asked questions

Why does Next.js say the revalidate value must be statically analyzable?

Because Next.js reads the export at build time without executing the module, so the value has to be a literal: revalidate = 600 is valid, revalidate = 60 * 10 is not, and neither is a number imported from another file.

Why is my revalidate interval being ignored in development?

In development, pages are always rendered on demand and never cached, so the interval has no visible effect — run next build followed by next start to observe it.

Which revalidate value wins when a layout and a page both set one?

The lowest value across every layout and page in the route determines the revalidation frequency of the entire route, and an individual fetch with a lower next.revalidate lowers it further.

More examples

The lowest value in the route wins

The route revalidates every 30 seconds, not every 3600. The lowest revalidate across the layouts and pages of a single route sets the frequency for the whole route, so the layout is re-rendered on the product page's schedule.

Example · typescript
// app/shop/layout.tsx
export const revalidate = 3600

export default function ShopLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

// app/shop/[sku]/page.tsx
export const revalidate = 30

export default async function ProductPage({
  params,
}: {
  params: Promise<{ sku: string }>
}) {
  const { sku } = await params
  const res = await fetch(`https://api.example.com/products/${sku}`)
  const product: { name: string; stock: number } = await res.json()

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.stock} in stock</p>
    </div>
  )
}

A value the build rejects, and the fix

Only the literal form survives the build. The two commented-out variants produce a build-time error because the revalidate value has to be readable without running the module.

Example · typescript
// app/feed/page.tsx

// Rejected: the compiler cannot statically analyze an expression.
// export const revalidate = 60 * 10

// Rejected: the value is not visible to static analysis.
// import { TEN_MINUTES } from '@/lib/timings'
// export const revalidate = TEN_MINUTES

// Accepted: a literal.
export const revalidate = 600

export default async function FeedPage() {
  const res = await fetch('https://api.example.com/feed')
  const items: { id: string; title: string }[] = await res.json()

  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  )
}

Opting a segment out of the static cache

revalidate = 0 renders every page under /account per request even when nothing in them reads a Request-time API. Fetches that explicitly set cache: 'force-cache' or a positive revalidate keep their own behaviour — this only changes the default for fetches that set no cache option.

Example · typescript
// app/(account)/layout.tsx
export const revalidate = 0

export default function AccountLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <main>{children}</main>
}

Discussion

  • Be the first to comment on this lesson.