From harness-claude
Manages Next.js's four cache layers (Data Cache, Full Route Cache, etc.) using fetch options, unstable_cache, revalidatePath, and revalidateTag to control freshness, performance, and invalidation. Use for stale data debugging and API optimization.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Control Next.js's four cache layers to balance freshness, performance, and cost
Explains Next.js 16 caching layers (Request Memoization, Data Cache, Full Route Cache, Router Cache), 'use cache' directive, fetch options, cacheLife/Tag, revalidation strategies, ISR, and debugging.
Guides Next.js Cache Components and Partial Prerendering (PPR) implementation with 'use cache' directives, cacheLife(), cacheTag(), invalidation via revalidateTag(), static/dynamic optimization, private caching, Route Handlers, and debugging.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Share bugs, ideas, or general feedback.
Control Next.js's four cache layers to balance freshness, performance, and cost
fetch(url, { next: { revalidate: 60 } }) to cache a fetch result for 60 seconds (ISR-style).fetch(url, { cache: 'no-store' }) to opt out of caching entirely — equivalent to always fetching fresh data.fetch(url, { next: { tags: ['posts'] } }) to attach a cache tag — call revalidateTag('posts') later to purge it.unstable_cache(fn, keyParts, { tags, revalidate }) to cache non-fetch data (database queries, third-party SDK calls).export const revalidate = 60 at the page or layout level as a per-segment default that applies to all fetches in that segment.export const dynamic = 'force-dynamic' to opt the entire route out of all caching — equivalent to SSR.revalidatePath('/posts') from a Server Action or Route Handler to purge all cache entries for that path.revalidateTag('tag') for targeted invalidation without knowing specific paths — one tag can cover many routes.// lib/posts.ts — cached database query with tags
import { unstable_cache } from 'next/cache';
import { db } from '@/lib/db';
export const getPosts = unstable_cache(
async () => db.post.findMany({ orderBy: { createdAt: 'desc' } }),
['posts-list'],
{ tags: ['posts'], revalidate: 3600 }
);
// app/api/revalidate/route.ts — webhook-triggered on-demand revalidation
import { revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const secret = request.headers.get('x-revalidate-secret');
if (secret !== process.env.REVALIDATE_SECRET) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { tag } = await request.json();
revalidateTag(tag);
return NextResponse.json({ revalidated: true });
}
Next.js App Router has four distinct caches that interact:
fetch() calls within a single request lifecycle. Automatic, not configurable. Reset per request.fetch() results across requests and deployments. Controlled by revalidate and cache fetch options. The primary cache for server-side data.revalidatePath().router.refresh() to purge.unstable_cache vs fetch cache: fetch() caching only applies to the native fetch function. Use unstable_cache to cache ORM queries (Prisma, Drizzle), Redis calls, or any async function. The API is identical in semantics — tags, revalidate, key parts.
Opt-out cascade: Using cookies(), headers(), or searchParams in a Server Component automatically opts that route into dynamic rendering, bypassing the Full Route Cache. A single dynamic function in a layout propagates dynamism to all child routes.
Debugging stale cache: Run next build and inspect the build output — routes marked ○ are static, λ are dynamic, ƒ are ISR. Use NEXT_PRIVATE_DEBUG_CACHE=1 environment variable in development to log cache hits and misses.
https://nextjs.org/docs/app/building-your-application/caching