nextjs-caching
Complete Next.js caching system with 'use cache' directive (Next.js 16). PROACTIVELY activate for: (1) Understanding 4 caching layers (Request Memoization, Data Cache, Full Route Cache, Router Cache), (2) Cache Components with 'use cache' directive, (3) fetch() caching options, (4) cacheLife() and cacheTag() functions, (5) Time-based revalidation, (6) On-demand revalidation with revalidatePath/revalidateTag, (7) Static generation with generateStaticParams, (8) Cache debugging. Provides: 'use cache' patterns, Cache Components, caching strategies, revalidation patterns, ISR setup, cache headers. Ensures optimal performance with correct cache invalidation.
What this skill does
## Quick Reference
| Cache Layer | Location | Duration | Purpose |
|-------------|----------|----------|---------|
| Request Memoization | Server | Per-request | Dedupe same fetches |
| Data Cache | Server | Persistent | Store fetch results |
| Full Route Cache | Server | Persistent | Pre-rendered HTML/RSC |
| Router Cache | Client | Session | Client-side navigation |
### Next.js 16: 'use cache' Directive
| Directive | Scope | Use Case |
|-----------|-------|----------|
| `'use cache'` | Default cache | General caching |
| `'use cache: remote'` | CDN/Edge cache | Static shared content |
| `'use cache: private'` | Per-user cache | User-specific data |
| Cache Function | Code | Purpose |
|----------------|------|---------|
| `cacheLife('hours')` | Duration | Set cache lifetime |
| `cacheTag('posts')` | Tagging | Enable targeted revalidation |
### Fetch Options
| Fetch Option | Code | Effect |
|--------------|------|--------|
| Default | `fetch(url)` | Cached indefinitely |
| No cache | `{ cache: 'no-store' }` | Always fresh |
| Revalidate | `{ next: { revalidate: 60 } }` | Stale after 60s |
| Tags | `{ next: { tags: ['posts'] } }` | Tag for invalidation |
| Revalidation | Code | Use Case |
|--------------|------|----------|
| `revalidatePath('/posts')` | Path | After mutation |
| `revalidateTag('posts')` | Tag | Invalidate tagged data |
| `router.refresh()` | Client | Refresh current route |
## When to Use This Skill
Use for **caching and revalidation**:
- Understanding Next.js caching behavior
- Configuring fetch caching strategies
- Setting up Incremental Static Regeneration (ISR)
- Implementing on-demand revalidation
- Debugging cache issues
**Related skills:**
- For data fetching: see `nextjs-data-fetching`
- For Server Actions: see `nextjs-server-actions`
- For static generation: see `nextjs-deployment`
---
# Next.js Caching
## Caching Overview
Next.js has four caching mechanisms:
| Mechanism | What | Where | Purpose | Duration |
|-----------|------|-------|---------|----------|
| Request Memoization | Return values of functions | Server | Avoid duplicate requests in component tree | Per-request |
| Data Cache | Data | Server | Store data across requests and deployments | Persistent |
| Full Route Cache | HTML and RSC payload | Server | Reduce rendering cost | Persistent |
| Router Cache | RSC payload | Client | Reduce server requests on navigation | Session |
## Request Memoization
### Automatic Deduplication
```tsx
// This is automatically memoized - only one fetch happens
async function getUser(id: string) {
const res = await fetch(`https://api.example.com/users/${id}`);
return res.json();
}
// Layout uses getUser
export default async function Layout({ children }: { children: React.ReactNode }) {
const user = await getUser('1'); // First call - fetches
return <div>{children}</div>;
}
// Page also uses getUser
export default async function Page() {
const user = await getUser('1'); // Second call - uses cached result
return <div>{user.name}</div>;
}
```
### Manual Memoization with cache()
```tsx
import { cache } from 'react';
// For non-fetch functions like database queries
export const getUser = cache(async (id: string) => {
return db.users.findUnique({ where: { id } });
});
// Both will use the same cached result
const user1 = await getUser('1');
const user2 = await getUser('1');
```
## Data Cache
### Caching with fetch()
```tsx
// Cached indefinitely (default)
const data = await fetch('https://api.example.com/data');
// Opt out of caching
const data = await fetch('https://api.example.com/data', {
cache: 'no-store',
});
// Time-based revalidation
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 }, // Revalidate every hour
});
// Tag-based caching
const data = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
});
```
### unstable_cache for Non-Fetch
```tsx
import { unstable_cache } from 'next/cache';
const getCachedUser = unstable_cache(
async (userId: string) => {
return db.users.findUnique({ where: { id: userId } });
},
['user'], // Cache key parts
{
revalidate: 3600, // 1 hour
tags: ['users'],
}
);
export default async function UserPage({ params }: { params: { id: string } }) {
const user = await getCachedUser(params.id);
return <div>{user?.name}</div>;
}
```
### Route Segment Config
```tsx
// Opt entire route out of caching
export const dynamic = 'force-dynamic';
// Set revalidation for entire route
export const revalidate = 60; // seconds
// Disable cache for all fetches in route
export const fetchCache = 'force-no-store';
// Force static generation
export const dynamic = 'force-static';
```
## Revalidation
### Time-Based Revalidation
```tsx
// Fetch with revalidation
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }, // Revalidate after 60 seconds
});
// Route-level revalidation
export const revalidate = 60;
```
### On-Demand Revalidation
```tsx
// app/actions.ts
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
// Revalidate a specific path
export async function updatePost(id: string, data: FormData) {
await db.posts.update({ where: { id }, data: { /* ... */ } });
// Revalidate the specific post page
revalidatePath(`/posts/${id}`);
// Revalidate the posts list
revalidatePath('/posts');
}
// Revalidate by tag
export async function createPost(data: FormData) {
await db.posts.create({ data: { /* ... */ } });
// All fetches with 'posts' tag will be revalidated
revalidateTag('posts');
}
```
### Revalidation API Route
```tsx
// app/api/revalidate/route.ts
import { revalidatePath, 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.REVALIDATION_SECRET) {
return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
}
const body = await request.json();
if (body.tag) {
revalidateTag(body.tag);
}
if (body.path) {
revalidatePath(body.path);
}
return NextResponse.json({ revalidated: true, now: Date.now() });
}
```
### Tag-Based Caching
```tsx
// Fetch with tags
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { tags: ['posts', 'content'] },
});
return res.json();
}
async function getPost(id: string) {
const res = await fetch(`https://api.example.com/posts/${id}`, {
next: { tags: ['posts', `post-${id}`] },
});
return res.json();
}
// Revalidate all posts
revalidateTag('posts');
// Revalidate specific post
revalidateTag('post-123');
// Revalidate all content
revalidateTag('content');
```
## Full Route Cache
### Static Routes
```tsx
// Automatically cached at build time
export default async function StaticPage() {
const data = await getData();
return <div>{data}</div>;
}
// Force static
export const dynamic = 'force-static';
```
### Dynamic Routes with generateStaticParams
```tsx
// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await db.posts.findMany({ select: { slug: true } });
return posts.map((post) => ({ slug: post.slug }));
}
// Pages are pre-rendered at build time
export default async function PostPage({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
return <article>{post.content}</article>;
}
```
### Opting Out
```tsx
// Dynamic route - not cached
export const dynamic = 'force-dynamic';
// Or use dynamic functions
import { headers, cookies } from 'next/headers';
export default async function Page() {
const headersList = await headers(); // Makes route dynamic
return <div>...</div>;
}
```
## Router Cache (Client-Side)
### Prefetching
```tsx
import Link from 'next/link';
// Prefetch on hover (default)
<Link href="/about">Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.