What caching does Next.js support?
"TLDR: This article provides a detailed introduction to the caching mechanisms supported by Next.js at different levels, including response header control at the API level, caching strategies for the Fetch function, function-level caching implemented via the Cache function, page-level ISR caching, and cross-instance KV distributed caching. It focuses on analyzing the technical implementation methods and applicable scenarios of each layer, such as setting browser/CDN cache freshness through the Cache-Control header, the force-cache forced edge caching feature, and key technical points like the react.cache higher-order function for optimizing repeated computation logic."
In Vercel, Next.js supports various levels of caching to enable serverless features.
API Level
Next.js supports exposing an API endpoint via route.ts in the /app/api directory. The caching approach at the API level is:
export async function GET() {
xxx
return NextResponse.json(
{ categories: formattedCategories },
{
status: 200,
headers: {
"Cache-Control": "public, max-age=60, stale-while-revalidate=30"
}
}
);
}
max-age=60: Usually set to 60s, indicating that the browser/Edge Cache caches for 60 seconds.stale-while-revalidate=30: After expiration, continues returning stale data while asynchronously updating in the background.- Caching can take effect at the Edge/CDN layer, reducing database pressure.
Fetch Function Caching
Next.js frequently uses fetch to retrieve data from other APIs (internal or external), which can also be cached:
export async function GET() {
const res = await fetch("https://api.example.com/data", { cache: "force-cache" });
const data = await res.json();
return NextResponse.json(data);
}
- The
cachevalues are:["no-store", "force-cache", "default"]. - This primarily affects internal
fetchcalls rather than controlling the final HTTP response. Combined withrevalidate, it enables ISR.
Cache Function Caching
Next.js also provides a cache function that supports function-level caching, avoiding repeated execution of function logic:
import { cache } from "react";
const getCategories = cache(async function () {
console.log("fetching categories");
const categories = await prisma.resource.groupBy({
by: ["category"],
_count: { id: true },
});
return categories;
});
export async function GET() {
const categories = await getCategories();
return NextResponse.json({ categories });
}
- The caching scope is limited to repeated calls within the same server instance, returning cached results directly without re-executing the function body.
- After a cold start of a Serverless instance, the cache is lost.
- It is well-suited for frequently called functions (such as database queries or compute-intensive logic).
Page-Level Caching
There is also a higher-level page caching approach, typically used for ISR, implemented via export const revalidate = xxx;.
However,
export const revalidate = xxx;does not seem to support computed expressions — you must specify a concrete revalidation time value, which is not very intelligent.
KV Caching
All the caching methods mentioned above are internal to a single Serverless instance. For cross-instance caching, a distributed cache middleware similar to Redis is needed, such as Vercel KV.