The Next.js fetch options extend the browser's built-in fetch function with server-side behavior you cannot express with the standard Web API. These options live in two places: the cache option, which decides whether a response is stored, and the next object, which carries revalidation and tag settings.
// app/posts/page.tsx
type Post = { id: number; title: string };
export default async function PostsPage() {
const res = await fetch("https://api.example.com/posts", {
cache: "force-cache",
next: { revalidate: 60 },
});
const posts: Post[] = await res.json();
return <p>{posts.length} posts</p>;
}This request is cached and revalidated every 60 seconds. Visitors inside that window get the stored response, and the next request after it expires fetches fresh data in the background.
These options describe the model used when Cache Components are not enabled. If your project turns on cacheComponents, prefer the use cache directive with cacheTag and cacheLife instead. Both models are explained in the caching mental model.
The cache option
The cache option controls whether and how a server-side fetch response is stored. Its default is auto no cache, which fetches once during build for prerendered routes and on every request when request-time APIs are used.
| Value | Behavior |
|---|---|
| auto no cache | Default. Fetches once during build for prerendered routes, every request when request-time APIs are used. |
| no-store | Fetches fresh on every request. |
| force-cache | Stores the response and reuses it while it is fresh. |
force-cache is the explicit opt-in that makes a request cacheable. It also works for POST requests and requests with cookie or authorization headers, which the default never caches.
Revalidation with next.revalidate
The next.revalidate option sets the cache lifetime in seconds. Use 0 to never cache and false to cache indefinitely.
// app/posts/page.tsx
export default async function PostsPage() {
const res = await fetch("https://api.example.com/posts", {
next: { revalidate: 3600 },
});
const posts = await res.json();
return <p>{posts.length} posts</p>;
}The response is reused for up to an hour. Conflicting options, like combining revalidate with no-store, are ignored and trigger a development warning.
Tags with next.tags
Tags let you invalidate a cached response on demand without waiting for the timer. Tag the request, then drop the tag after a mutation.
// app/lib/posts.ts
export async function getPosts() {
const res = await fetch("https://api.example.com/posts", {
next: { tags: ["posts"] },
});
return res.json();
}After a write, call revalidateTag with the same tag to invalidate every response that carries it. Since Next.js 16 it takes a second argument, and the recommended value is the max profile, which serves the stale response while a fresh one loads in the background. The choice between this and updateTag is covered in revalidateTag vs updateTag.
// app/posts/actions.ts
"use server";
import { revalidateTag } from "next/cache";
import { requireEditor } from "@/lib/auth";
export async function refreshPosts() {
await requireEditor();
revalidateTag("posts", "max");
}The next visit to a page that uses the tag gets fresh data, and every page carrying that tag updates together. The auth check is there because an exported Server Action is a public endpoint that anyone can call, so it needs the same session check a route handler would.
The single-argument form, revalidateTag("posts"), still runs but is deprecated, so update existing calls when you touch them.
Deduplication is separate
Caching stores a response across requests, and you opt into it with the options above. Deduplication is different: identical GET requests are memoized automatically inside a single render pass, and that memo never survives past the render. So deduplication needs no options, while persistence always does.
A good rule of thumb is to tag anything that changes when a user mutates data, and to leave a revalidate timer on content that only needs periodic freshness.
Rune AI
Key Insights
- The cache option controls whether a fetch response is stored.
- next.revalidate sets the cache lifetime in seconds.
- next.tags pairs with revalidateTag for on-demand invalidation.
- Caching is opt-in, so set force-cache to store a response.
- These options describe the model used without Cache Components.
Frequently Asked Questions
Do these fetch options work under Cache Components?
Does fetch cache by default?
What is the difference between caching and memoization?
Conclusion
Next.js adds cache, next.revalidate, and next.tags to the standard fetch API. cache decides whether a response is stored, revalidate sets its lifetime, and tags let you invalidate it on demand.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.