Next.js 16 Caching Explained: Revalidation, Tags, Draft Mode & Real Production Patterns

Published: (February 22, 2026 at 05:12 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

🎯 What We’re Building

A production‑ready mental model for caching:

  • Static + dynamic control using fetch
  • Tag‑based invalidation
  • On‑demand revalidation
  • Draft mode for preview workflows
  • Real‑world patterns I actually use

No guesswork. No accidental stale pages.

🧠 First: The New Mental Model

In Next.js 16, caching is no longer “page‑based”.
It’s data‑based. The unit of caching is now the fetch() call.

  • Every fetch can be cached or dynamic.
  • Every fetch can define revalidation rules.
  • Every fetch can be invalidated via tags.

This is cleaner and more scalable.

🔥 1. Controlling Cache with fetch

Default behavior

const res = await fetch("https://api.example.com/posts");

By default, this response is cached in production.

Static with Revalidation (ISR‑style)

const res = await fetch("https://api.example.com/posts", {
  next: { revalidate: 60 }
});
  • Cache this response.
  • Revalidate every 60 seconds.

Replaces older ISR patterns with finer granularity.

Fully Dynamic (No Cache)

const res = await fetch("https://api.example.com/posts", {
  cache: "no-store"
});

Forces dynamic rendering. Use when:

  • User‑specific data
  • Authenticated dashboards
  • Rapidly changing metrics

🏷️ 2. The Real Upgrade: Cache Tags

Next.js 16 lets you tag cached fetches:

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

The cache entry is now associated with the "posts" tag, enabling manual invalidation.

🚀 3. On‑Demand Revalidation with Tags

Create a route handler that revalidates a tag:

// app/api/revalidate/route.ts
import { revalidateTag } from "next/cache";

export async function POST() {
  revalidateTag("posts");
  return Response.json({ revalidated: true });
}

Calling this endpoint instantly invalidates all cached fetches tagged "posts"—precise, not page‑level, not global.

🧪 4. Combining Revalidation + Tags (Best Pattern)

const res = await fetch("https://api.example.com/posts", {
  next: {
    revalidate: 3600,
    tags: ["posts"]
  }
});
  • Automatic hourly refresh
  • Manual invalidation when needed
  • No unnecessary rebuilds

📝 5. Draft Mode for Preview Workflows

Enabling draft mode

import { draftMode } from "next/headers";

export async function GET() {
  draftMode().enable();
  return Response.redirect("/admin");
}

Using draft mode in a page

import { draftMode } from "next/headers";

export default async function Page() {
  const { isEnabled } = draftMode();

  const res = await fetch("https://api.example.com/posts", {
    cache: isEnabled ? "no-store" : "force-cache"
  });

  const data = await res.json();

  return { data.title };
}

When draft mode is active:

  • Cache is bypassed
  • Unpublished changes are visible

When off, normal caching resumes.

⚙️ 6. Production Pattern I Actually Use

Use caseCache config
Public contentnext: { revalidate: 600, tags: ["posts"] }
Admin updatesrevalidateTag("posts")
User dashboardscache: "no-store"
Preview routesdraftMode + no-store

Result: performance, freshness, precision, scalability.

⚠️ Common Mistakes I Made

  • Mixing cache: "no-store" with revalidate
  • Forgetting tags and trying to revalidate entire paths
  • Assuming dev mode reflects production caching
  • Over‑invalidating

Tip: Dev mode behaves differently. Always test caching in a production build:

next build
next start

🧩 How This Changes Everything

Before Next.js 16, caching felt page‑based and indirect.
Now it’s:

  • Declarative
  • Granular
  • Fully controllable

The shift from page ISR to fetch‑level caching is a major architectural improvement.

🏁 Final Thoughts

Next.js 16 doesn’t just improve caching—it makes it predictable.
If you understand:

  • fetch cache control
  • revalidate
  • tags
  • revalidateTag()
  • draftMode()

you control performance instead of guessing it.

If this clarified things for you, feel free to share it with other frontend engineers battling stale data.
I’d love to see any interesting caching patterns you’ve built in Next.js 16.

More deep dives coming.

Check me out at .

0 views
Back to Blog

Related posts

Read more »

How to Read HTTP Headers

What Are HTTP Headers? When you visit a website, your browser sends a request to the server, and the server sends back a response. Both the request and the res...