Caching and revalidation
A cache is a saved result that is served again without redoing the work. Next.js can cache fetch responses and finished pages. Next.js 16 has two caching models: the regular one, which works in a new project right away, and Cache Components, which you turn on with a setting. This lesson covers the regular model; Cache Components get a lesson of their own.
Important: development has almost no cache
npm run dev renders pages again on every request. To see how caching really works, build and start the project:
npm run build
npm run startfetch by default
// app/external/page.tsx (fragment)
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=3');This request is not stored in the cache. There is a catch, though: if the page uses nothing that depends on the visitor's request (cookies, headers, searchParams), Next.js renders the whole page at build time and serves the ready HTML. The data inside it is then "frozen" until the next build too. More on this in the lesson on static and dynamic rendering.
Enabling the cache for fetch
| Option | What happens |
|---|---|
cache: 'force-cache' | The response is stored and reused |
cache: 'no-store' | The request runs every time |
next: { revalidate: 60 } | The response is kept for 60 seconds, then refreshed |
next: { tags: ['posts'] } | The response gets a tag for manual clearing |
// app/external/page.tsx (fragment)
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=3', {
next: { revalidate: 60, tags: ['posts'] },
});The first visitor gets fresh data, the following ones get the stored data for a minute. A request after that minute serves the old version and starts a refresh in the background.
A lifetime for the whole page
Instead of configuring each request, you can set a time for the page by exporting a revalidate variable:
// app/posts/page.tsx (fragment)
export const revalidate = 60;The /posts page is rendered again at most once a minute, even if the data comes from a database rather than fetch.
Clearing the cache after changes
When a user has added a post, you do not want to wait a minute. next/cache has three functions for this:
| Function | What it does | Where to call it |
|---|---|---|
revalidatePath('/posts') | Marks the page as stale | Server action, route handler |
updateTag('posts') | Drops tagged data at once; the next request waits for fresh data | Server actions only |
revalidateTag('posts', 'max') | Marks tagged data as stale, refreshes in the background | Server action, route handler |
// app/posts/actions.ts (fragment)
import { revalidatePath, updateTag } from 'next/cache';
revalidatePath('/posts');
updateTag('posts');After their own edit a user should see the result immediately, so actions use revalidatePath or updateTag. revalidateTag fits when a short delay does no harm, such as an update coming from an external system. In Next.js 16 revalidateTag requires a second argument — a profile, usually 'max'; the old one-argument form is deprecated.
The browser cache
When navigating with Link, the browser remembers layouts, but requests pages from the server again by default. The exception is the Back and Forward buttons.
Common mistake
Testing caching in npm run dev and concluding that "the cache does not work" or "works differently". Development behaves differently. Check caching only after npm run build and npm run start.