Using `fetch` in Next.js: Extended Options Explained

Next.js extends the built-in fetch with server-side cache, revalidation, and tag options. Learn what each option does and when to use it.

7 min read

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.tsxApp.tsx
// 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.

Which caching model is this

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.

ValueBehavior
auto no cacheDefault. Fetches once during build for prerendered routes, every request when request-time APIs are used.
no-storeFetches fresh on every request.
force-cacheStores 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.tsxApp.tsx
// 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.

typescripttypescript
// 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.

typescripttypescript
// 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

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.
RunePowered by Rune AI

Frequently Asked Questions

Do these fetch options work under Cache Components?

They describe the model used when Cache Components are disabled. With cacheComponents enabled, prefer the use cache directive with cacheTag and cacheLife.

Does fetch cache by default?

No. Caching is opt-in. Set cache to force-cache, or give the request a revalidate or tags value, to store the response.

What is the difference between caching and memoization?

Caching stores a response across requests. Memoization only stops the same fetch from firing twice within a single render, and it never persists.

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.