Skip to content

The fetch API in Next.js

Next.js extends the browser's native fetch() function with extra options for controlling caching and revalidation. This lesson focuses specifically on that extended API and its most important options.

fetch() with Next.js-Specific Options

Calling fetch() inside a Server Component works exactly like the standard Web API you already know, but Next.js augments it with a second, Next.js-specific options object (nested under a next key) that controls how the result is cached and when it should be considered stale.

By default, fetch() requests in Next.js are cached indefinitely at build/request time unless you explicitly opt out — this is one of the most surprising behaviors for newcomers coming from plain React, where every fetch() call is always a fresh network request.

// Cached indefinitely (default-like behavior)
const res = await fetch("https://api.example.com/posts");

// Revalidate every 60 seconds (Incremental Static Regeneration)
const res2 = await fetch("https://api.example.com/posts", {
  next: { revalidate: 60 },
});

// Never cache — always fetch fresh data
const res3 = await fetch("https://api.example.com/posts", {
  cache: "no-store",
});

The next: { revalidate } option is what powers Incremental Static Regeneration for individual fetch calls, covered fully in its own lesson.

fetch() Option Reference

fetch(url, {
  cache: "force-cache" | "no-store",
  next: {
    revalidate: number | false,
    tags: string[],
  },
})
  • cache: "force-cache" caches the response and reuses it for subsequent identical requests.
  • cache: "no-store" disables caching entirely for that specific request.
  • next.revalidate sets a time (in seconds) after which the cached result becomes stale and refetches.
  • next.tags assigns cache tags you can later invalidate on demand with revalidateTag().

fetch() Caching Cheat Sheet

Which option to reach for based on how fresh your data needs to be.

Goal Option to Use
Cache indefinitely, rarely-changing data Default fetch() behavior, or cache: "force-cache"
Always fresh, per-request data cache: "no-store"
Refresh automatically every N seconds next: { revalidate: N }
Refresh only when explicitly told to next: { tags: [...] } + revalidateTag()
Force a route to always be dynamic export const dynamic = "force-dynamic";

Choosing Between revalidate and no-store

no-store is the right choice for data that must always be current on every request — like a live stock price or a personalized dashboard. revalidate is better for content that changes occasionally, like a blog post or product listing, where serving slightly stale data for a short window is an acceptable trade-off for much better performance.

On-Demand Revalidation with Tags

Tagging a fetch call lets you invalidate its cached result on demand — for example, immediately after a Server Action updates the underlying data — rather than waiting for a fixed time interval to pass.

// Tagging a fetch call
const res = await fetch("https://api.example.com/posts", {
  next: { tags: ["posts"] },
});

// Elsewhere, after a mutation:
import { revalidateTag } from "next/cache";
revalidateTag("posts");

Any cached fetch() result tagged "posts" is invalidated immediately, without waiting for its revalidate timer.

Common Mistakes

  • Being surprised that fetch() returns stale data, without realizing Next.js caches it by default in many cases.
  • Using no-store everywhere out of caution, losing the performance benefits of caching for content that rarely changes.
  • Forgetting next.tags and revalidateTag() must use the exact same tag string to match.
  • Not knowing export const dynamic = "force-dynamic" exists when a whole route needs to opt out of caching entirely.

Key Takeaways

  • Next.js extends fetch() with a next options object for caching and revalidation.
  • cache: "no-store" always fetches fresh data; cache: "force-cache" reuses cached results.
  • next.revalidate sets a time-based staleness window for Incremental Static Regeneration.
  • next.tags combined with revalidateTag() enables on-demand cache invalidation.
  • Understanding these defaults is essential — unexpected caching is a very common source of confusion.

Pro Tip

When a page seems to be showing outdated data during development, check its fetch() calls for missing cache options before assuming there's a bug elsewhere — Next.js's fetch caching is powerful but easy to overlook when you're used to plain browser fetch() behavior.