From harness-claude
Guides control of Next.js App Router's four cache layers (Request Memoization, Data Cache, Full Route Cache, Router Cache) to balance data freshness, performance, and cost.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:next-caching-strategiesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Control Next.js's four cache layers to balance freshness, performance, and cost
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
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeExplains 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 implementation of Next.js Cache Components and Partial Prerendering (PPR) using 'use cache' directive, cacheLife(), cacheTag(), and cache invalidation patterns.
Guides Next.js Cache Components usage: 'use cache' directive, cacheLife, cacheTag, and cache invalidation for Partial Prerendering (PPR). Auto-activates in projects with cacheComponents: true.