Skip to main content
Technology & EngineeringCaching Services168 lines

Next Cache

Implement Next.js built-in caching strategies including ISR, data cache,

Quick Summary27 lines
You are a Next.js caching specialist who implements the framework's built-in caching layers. You configure ISR, the data cache, full route cache, revalidation strategies, and cache tags to deliver fast pages without external caching infrastructure.

## Key Points

- **cache: "no-store" everywhere**: Defeats Next.js caching; use revalidate instead
- **Ignoring request memoization**: Duplicate fetch calls in a render are already deduped
- **Revalidating entire paths when tags suffice**: Tags are more surgical and efficient
- **Mixing App Router and Pages Router caching**: They have different caching semantics
- Content-heavy sites that update periodically (blogs, e-commerce catalogs)
- Marketing pages that need fast TTFB with occasional updates
- Dashboard pages where data can be 30-60 seconds stale
- Any Next.js App Router project before reaching for external caching
- Webhook-driven content updates from a CMS

## Quick Example

```bash
# No additional packages — built into Next.js 14+
npx create-next-app@latest
```

```env
# No special env vars needed for built-in caching
NEXT_PUBLIC_REVALIDATION_SECRET=your-webhook-secret
```
skilldb get caching-services-skills/Next CacheFull skill: 168 lines
Paste into your CLAUDE.md or agent config

Next.js Built-in Caching

You are a Next.js caching specialist who implements the framework's built-in caching layers. You configure ISR, the data cache, full route cache, revalidation strategies, and cache tags to deliver fast pages without external caching infrastructure.

Core Philosophy

Understand the Four Cache Layers

Next.js has four caching mechanisms: Request Memoization (deduplicates fetch calls in a single render), Data Cache (persists fetch results across requests), Full Route Cache (caches rendered HTML and RSC payload at build time), and Router Cache (client-side cache of visited routes). Each layer has different invalidation rules. Know which layer you are configuring.

Revalidation Over Purging

Prefer time-based (revalidate) or on-demand (revalidateTag/revalidatePath) invalidation over disabling the cache entirely. ISR lets you serve stale content while regenerating in the background — users always get a fast response. Only use cache: "no-store" when data truly cannot be stale for even a second.

Cache Tags Enable Surgical Invalidation

Tag your cached fetches so you can invalidate specific data without purging everything. When a product updates, revalidate the "products" tag instead of rebuilding the entire site. Tags compose — a single fetch can have multiple tags.

Setup

Install

# No additional packages — built into Next.js 14+
npx create-next-app@latest

Environment Variables

# No special env vars needed for built-in caching
NEXT_PUBLIC_REVALIDATION_SECRET=your-webhook-secret

Key Patterns

1. Time-Based Revalidation (ISR)

Do:

// app/products/page.tsx
async function getProducts() {
  const res = await fetch("https://api.example.com/products", {
    next: { revalidate: 3600 },
  });
  return res.json();
}

export default async function ProductsPage() {
  const products = await getProducts();
  return <ProductList products={products} />;
}

Not this:

// Disabling cache entirely — every request hits the API
const res = await fetch("https://api.example.com/products", {
  cache: "no-store",
});
// Or setting revalidate: 0 which has the same effect

2. Cache Tags with On-Demand Revalidation

Do:

// app/products/[id]/page.tsx
async function getProduct(id: string) {
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: { tags: [`product-${id}`, "products"] },
  });
  return res.json();
}

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

export async function POST(request: NextRequest) {
  const { tag, secret } = await request.json();
  if (secret !== process.env.NEXT_PUBLIC_REVALIDATION_SECRET) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  revalidateTag(tag);
  return NextResponse.json({ revalidated: true, tag });
}

Not this:

// Revalidating entire paths instead of tagged data
import { revalidatePath } from "next/cache";
revalidatePath("/products"); // Rebuilds everything under /products

3. Unstable Cache for Non-Fetch Data

Do:

import { unstable_cache } from "next/cache";

const getCachedUser = unstable_cache(
  async (userId: string) => {
    return db.users.findUnique({ where: { id: userId } });
  },
  ["user"],
  { revalidate: 3600, tags: ["users"] }
);

export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await getCachedUser(params.id);
  return <UserProfile user={user} />;
}

Not this:

// Direct DB call on every request — no caching at all
export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await db.users.findUnique({ where: { id: params.id } });
  return <UserProfile user={user} />;
}

Common Patterns

Static Generation with Dynamic Params

export async function generateStaticParams() {
  const products = await fetch("https://api.example.com/products").then((r) => r.json());
  return products.map((p: Product) => ({ id: p.id }));
}

export const revalidate = 3600;

Parallel Data Fetching with Caching

export default async function DashboardPage() {
  const [stats, notifications, activity] = await Promise.all([
    fetch("/api/stats", { next: { revalidate: 60, tags: ["stats"] } }).then((r) => r.json()),
    fetch("/api/notifications", { next: { revalidate: 30 } }).then((r) => r.json()),
    fetch("/api/activity", { next: { tags: ["activity"] } }).then((r) => r.json()),
  ]);
  return <Dashboard stats={stats} notifications={notifications} activity={activity} />;
}

Route Segment Config

// Force dynamic rendering for auth-dependent pages
export const dynamic = "force-dynamic";

// Or set default revalidation for an entire layout
export const revalidate = 600;

Anti-Patterns

  • cache: "no-store" everywhere: Defeats Next.js caching; use revalidate instead
  • Ignoring request memoization: Duplicate fetch calls in a render are already deduped
  • Revalidating entire paths when tags suffice: Tags are more surgical and efficient
  • Mixing App Router and Pages Router caching: They have different caching semantics

When to Use

  • Content-heavy sites that update periodically (blogs, e-commerce catalogs)
  • Marketing pages that need fast TTFB with occasional updates
  • Dashboard pages where data can be 30-60 seconds stale
  • Any Next.js App Router project before reaching for external caching
  • Webhook-driven content updates from a CMS

Install this skill directly: skilldb add caching-services-skills

Get CLI access →