Skip to content
Back to skills

Cache Components

ASecurity

Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). Use when implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, instant navigation validation, 'use cache: private', pass-through/interleaving patterns, GET Route Handler caching, debugging cache issues, and reviewing Cache Component implementations.

  • 67 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 9, 2026
ai-agentsgoshellreactnextjsnodedebuggingapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add laguagu/claude-code-nextjs-skills --skill cache-components --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cache Components?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cache Components
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/laguagu-cache-components/badge)](https://www.skillsdirectory.com/skills/laguagu-cache-components)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cache-components
description: "Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). Use when implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, instant navigation validation, 'use cache: private', pass-through/interleaving patterns, GET Route Handler caching, debugging cache issues, and reviewing Cache Component implementations."
metadata:
  version: "1.0"
---

# Cache Components

Keep the project's caching mode unless migration is requested. Confirm the
installed Next.js version and `cacheComponents` configuration before applying
these patterns.

Read the relevant guide in `node_modules/next/dist/docs/` first (bundled since
16.2; in a monorepo, use the app's own `next` package). Web paths map to files
with numbered folders, so find them by name: `.../functions/cacheLife` is
`01-app/03-api-reference/04-functions/cacheLife.md`. Under `01-app/`, start
with `02-guides/migrating-to-cache-components.md`,
`01-getting-started/08-caching.md` and `02-guides/instant-navigation.md`.
Error pages under `/docs/messages` are not bundled; `next dev`/`next build`
print the fix options inline. Without local docs, use the
[official caching docs](https://nextjs.org/docs/app/getting-started/caching).
Exact exports and signatures are also in `node_modules/next/cache.d.ts`.

The goal is a useful prerendered shell with freshness and authorization rules
that remain correct after mutations, deploys and client navigation.

## Facts older examples get wrong

- Next.js 16 enables the mode with top-level `cacheComponents: true`. It replaces
  `experimental.dynamicIO`, `experimental.useCache` and `experimental.ppr`;
  `cacheLife`/`cacheTag` import from `next/cache` without `unstable_`.
- With it enabled, segment exports `dynamic`, `revalidate`, `fetchCache` and
  `dynamicParams` are errors, and Edge runtime is unsupported. `revalidate = N`
  becomes `cacheLife` (nearest or custom profile) inside `use cache`;
  `force-dynamic` and `fetchCache` are unnecessary; fetch
  `next: { revalidate, tags }` becomes `cacheLife`/`cacheTag` in a `use cache`
  function; request data goes under Suspense; `dynamicParams = false` becomes
  `notFound()` for unknown params.
- Existing `fetch` and `unstable_cache` caching still works as a separate layer,
  so do not mechanically rewrite every read. Its persistence across deployments
  and instances depends on retained/shared storage; self-hosted instances do not
  share it by default. `use cache` is in-memory by default; even a durable
  handler cannot reuse entries when the build/deployment ID changes.
- `generateStaticParams` must return at least one param: `[]` fails the build,
  and removing the export renders the route on every request.
- `revalidate: 0` or `expire` under 5 minutes makes a request-time hole instead
  of prerendered output; of the presets only `seconds` does. 16.3 adds `stale`
  thresholds (under 30 s: not prerendered; under 5 min: not in the App Shell).
- Synchronous `new Date()`, `Math.random()` or `crypto.randomUUID()` during
  prerender is an error: capture it in `use cache`, or defer with `await io()`
  (`next/cache`, 16.3+) or `await connection()` (`next/server`) under Suspense.
- `use cache: private` was experimental through 16.2. It runs at request time,
  stays out of the static shell and accepts no custom handler.
- `export const instant` (16.3; `unstable_instant` in 16.2) asks dev/build to
  validate instant navigation into a segment. `instant = false` permits a
  blocking route; it does not clear synchronous-IO errors.

For whole-app adoption or instant-navigation work, Next.js publishes workflow
skills (`next-cache-components-adoption`, `next-cache-components-optimizer`)
in `vercel/next.js/skills`; the bundled migration guide covers the same steps.

## Shape of one route

`app/posts/page.tsx`, checked against next 16.3.8 with `cacheComponents: true`
(`tsc --noEmit`, `next build`, the action under `next start`). `@/lib/*` is
project code.

```tsx
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag, updateTag } from 'next/cache'
import { insertPost, listPosts } from '@/lib/posts' // project code
import { requireEditor } from '@/lib/auth' // throws unless the session may edit

async function getPosts() {
  'use cache' // shared entry: no cookies/headers/searchParams inside
  cacheLife('hours')
  cacheTag('posts')
  return listPosts()
}

async function Theme() {
  const theme = (await cookies()).get('theme')?.value // request-time, outside the cache
  return <p>Theme: {theme ?? 'system'}</p>
}

async function addPost(formData: FormData) {
  'use server'
  await requireEditor()
  const title = String(formData.get('title') ?? '').trim()
  if (!title) return
  await insertPost(title)
  updateTag('posts') // Server Actions only; the next read waits for fresh data
}

export default async function Page() {
  const posts = await getPosts() // cached, so it can be part of the static shell
  return (
    <>
      <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
      <Suspense fallback={<p>Theme: …</p>}><Theme /></Suspense>
      <form action={addPost}><input name="title" /><button>Add</button></form>
    </>
  )
}
```

## Decide the boundary

- Cache reusable results when the product's freshness and data policy permit.
  Static content needs no directive just to be static.
- A plain `use cache` scope cannot read cookies, headers, searchParams or call
  `connection()`, including indirectly through helpers. Read those outside
  and pass authorized, serializable values.
  Account/tenant IDs must participate in the cache key when output depends on them.
  On a dynamically rendered route, the `next-request-in-use-cache` error can
  pass `next build` and surface only under `next start`.
- Keep uncached I/O and request-only content below appropriate Suspense
  boundaries. Suspense supplies fallback UI; it does not itself make synchronous
  work dynamic.
- Evaluate private/remote variants against installed docs and deployment
  handlers. Private caching is not a compliance guarantee; remote caching is not
  a consistency protocol.

## Define freshness and invalidation

Choose lifetimes from actual acceptable staleness. Named profiles can be
overridden by the project; read its config before assuming a duration.

Use tags when invalidation must reach the same entity across routes, path
invalidation for route-specific output, or expiry where that meets the contract.
Authenticate, authorize and validate before mutations. Invalidate only affected
cached data:

- `updateTag`: immediate expiry/read-your-writes, Server Actions only.
- `revalidateTag(tag, 'max')`: stale-while-revalidate on a later visit.
- `revalidateTag(tag, { expire: 0 })`: immediate expiry where a webhook/Route
  Handler needs it. The one-argument form is deprecated.

These tag APIs can also apply to tagged fetch data; their availability does not
by itself mean Cache Components is enabled.

## Read for the problem

- [API boundaries](REFERENCE.md): keys, serialization, lifetimes, handlers and route APIs.
- [Composition](PATTERNS.md): tenant isolation, nested caches, pass-through and dynamic params.
- [Troubleshooting](TROUBLESHOOTING.md): diagnose blocking, stale or inconsistent output.

Run the production build, then exercise cold/direct navigation, client
navigation, the relevant mutation and subsequent read under `next start`.
Check tenant isolation and multi-instance invalidation when applicable.
A passing build cannot establish those behaviors.

Files in this skill

  • PATTERNS.md23.4 KB
  • REFERENCE.md42.3 KB
  • SKILL.md15.4 KB
  • TROUBLESHOOTING.md22.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.