KolarclubPersonal website

Next.js 15 App Router Caching, Part 3: Full Route Cache

How static rendering, dynamic APIs, cached data, and revalidation determine whether a Next.js 15 route uses the Full Route Cache.

This article was originally published on the previous Kolarclub website on 30 April 2025. It was transferred here on 22 July 2026 and revised for clarity and technical accuracy.

The first two articles in this series covered Request Memoization and the Data Cache. This final part moves one level up: from caching individual data requests to caching the rendered result of an entire route.

What the Full Route Cache Stores

When Next.js statically renders a route, it can store:

  • The route’s HTML.
  • The React Server Component payload used for client-side navigation and updates.

Later visitors can receive that stored output without rendering the route again on the server. This reduces server work and usually improves response time.

The Full Route Cache is a server-side Next.js abstraction. Its physical storage depends on the deployment platform, so application code should not rely on a particular file-system location.

Static vs. Dynamic Rendering

The Full Route Cache applies to statically rendered routes. Dynamically rendered routes are rendered for each request and are not stored in it.

// app/page.tsx

export const dynamic = 'auto' // default
// export const dynamic = 'force-static'
// export const dynamic = 'force-dynamic'

export default async function Page() {
  return <main>...</main>
}

The three common dynamic values are:

  • auto — let Next.js choose based on the route’s APIs and data access.
  • force-static — require static rendering.
  • force-dynamic — render on every incoming request.

Prefer the default and configure caching close to the data request when possible. The route-level option is useful when the whole page truly needs one rendering strategy.

What Makes a Route Dynamic

A route opts out of the Full Route Cache when it depends on request-time information or explicitly requests dynamic rendering. Common examples include:

  • cookies
  • headers
  • the page’s searchParams
  • draftMode
  • fetch with cache: 'no-store'
  • export const dynamic = 'force-dynamic'
  • export const revalidate = 0

Using one uncached request does not prevent other requests in the same route from using the Data Cache. This allows a useful hybrid: dynamically render the page while caching selected shared data.

Incremental Static Regeneration

A static route does not have to remain unchanged until the next deployment. Set a revalidation interval to use Incremental Static Regeneration:

export const revalidate = 60

export default async function Page() {
  return <main>...</main>
}

The cached route can be served during the 60-second window. After it becomes stale, a later request triggers regeneration, and Next.js stores the new output when rendering succeeds.

You can also revalidate on demand:

'use server'

import { revalidatePath } from 'next/cache'

export async function publishPost() {
  // Save the post first.
  revalidatePath('/blog')
}

On-demand revalidation is a better fit when freshness is tied to an event such as publishing a post or updating a product.

How the Data Cache Affects the Route Cache

The Data Cache stores data. The Full Route Cache stores rendered output. They are separate, but revalidating cached data can cause Next.js to render the affected route again and replace its cached output.

The reverse is not necessarily true. Clearing or redeploying the rendered route does not automatically mean that every persistent data entry has been invalidated.

This distinction explains several useful combinations:

Route rendering Data request Result
Static Cached Route and data can both be reused
Static with revalidation Cached with revalidation Route can be regenerated with fresh data
Dynamic Cached Route renders per request, selected data is reused
Dynamic Uncached Route renders and data is fetched per request

Dynamic APIs and Draft Mode

draftMode() reads request cookies, so a preview route that uses it is dynamic. That is normally what you want: editors should see request-specific draft content instead of a public cached page.

import { draftMode } from 'next/headers'

export default async function PostPage() {
  const { isEnabled } = await draftMode()
  const post = await cms.getPost({ draft: isEnabled })

  return <Post post={post} />
}

The published version and the preview experience can use different routes or caching strategies when you need strict separation.

Full Route Cache Is Not the Router Cache

The names are easy to confuse:

  • Full Route Cache lives on the server and stores output for statically rendered routes.
  • Router Cache lives in the browser and stores React Server Component payloads for faster navigation during a user session.

Invalidating one can affect the other in some workflows, but they solve different problems.

A Practical Decision Process

For each route, ask:

  1. Does the output depend on the current request, user, cookie, or query string?
  2. Can the route be generated ahead of time?
  3. If it is static, how soon must changes appear?
  4. Should freshness be time-based or triggered by a publishing event?

If the output is public and changes infrequently, static rendering with revalidation is usually a strong starting point. If it is personal or request-specific, dynamic rendering is the safer model.

For the complete interaction between the caches, see the Next.js 15 caching guide.