Officialvercel/next.js6 files

Next Cache Components Optimizer

>

Specification
Skill ID
vercel/next.js/next-cache-components-optimizer
Publisher
vercel
Repository
next.js
Installs
217
Files
6
Synced
Sep 16, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

vercel/next.js/next-cache-components-optimizerInstalls these files
  • SKILL.md
  • reference/patterns.md
  • reference/real-app-patterns.md
  • reference/red-test-robustness.md
  • rig-template.md
  • test-template.md

What this skill tells the agent

next-cache-components-optimizer

Set up an agentic optimization loop that drives a Next.js route from "not instant" to "instant" and keeps it there. The loop is test-driven: encode the goal as a failing @next/playwright instant() test, work it to green, and ship the test as the regression guard. Run it once per target route. Work the phases P → G in order; each ends in a gate. Fix recipes live in two lazily-read references — reference/patterns.md (before→after for each blocker type) and reference/real-app-patterns.md (parallel routes, auth gates, the empty-shell and responsive-skeleton failure modes). Read one only when its phase points there.

What is invariant, and what is yours

One thing here is fixed. The rest is yours. Read this before treating any command, platform, or env var below as a requirement.

  • Invariant: the verification loop. Maximizing the shell is worthless unless you can prove it. The proof is an automated check: under a lock that gates dynamic data, the static shell still commits. RED shows the gap, GREEN shows it closed, the test ships as the regression guard. It must run on a production-like build and must not be able to pass vacuously. Stand the loop up once; every later optimization is then verifiable by construction. The loop is the deliverable, not any one route.
  • The mechanism: `@next/playwright` `instant()`. This skill uses `instant()` as a ruler, not a stopwatch (phase A). It comes from @next/playwright (installed alongside @playwright/test, on the same release line as next), so it isn't tied to any host. Keep it. Timing a navigation by hand is too flaky to trust, and is the failure mode this skill exists to prevent.
  • Yours: the rig. How you build, deploy, authenticate, configure Playwright, and loop belongs to your stack, not to this skill. A local next build && next start, a CI/staging container, and a per-push preview deploy are equally valid rigs; the verdict comes from the build, never the platform. Phase 0 maps the invariant onto your repo. Read every platform name, env-var spelling, and command below as an example to translate, not a requirement.

Two navigations, two loading states

A route reaches the user two ways, and both must be instant:

  • Initial load (hard navigation) commits the route's prerendered static shell; deferred parts stream in behind their loading skeletons (Suspense fallbacks, loading.tsx).
  • Client-side navigation (soft navigation) commits the destination's prefetched App Shell — the <Link> default under Partial Prefetching — re-rendering only the segments that change.

The fix patterns are identical for both; the test differs only in how the navigation is driven ("Driving the navigation in tests" below). The two shells can differ; guard the one you ship, both when both matter (reference/real-app-patterns.md).

Goal

Maximizing the static shell is the optimization objective: the most meaningful prerendered content commits immediately, and only genuinely per-request data streams in afterward. The shipped test deterministically encodes present ∧ instant; non-blank is the additional bar the workflow enforces by judgment (D1/D2/E), because an instant() pass alone is satisfied by a blank fallback={null} shell (the empty-shell failure mode, reference/real-app-patterns.md).

instant() is a ruler, not a stopwatch: assert that the shell appears under the lock; do not time it. A trustworthy verdict requires a production build (phase A).

The GREEN under the lock is the deterministic verdict; each gate keeps it trustworthy.

Reporting to the user

This loop is meant to run unattended, so it doesn't stop to ask between steps. Work the navigation the user named, finish it, and stop. What matters is how you word and present the results, not how often you interrupt. The mechanics below — the rig, RED, GREEN, the gates — are your scaffolding; the user never needs to hear those words.

  • Speak their language. Describe the gap and the result in terms of what the user sees: "navigating to the dashboard waited on the charts query before anything painted; now the layout and skeletons paint instantly and the charts stream in" — not RED/GREEN, the lock, or the phase letters.
  • Show, don't tell. When you report a route, drive the browser (or attach before/after screenshots) so the user watches the shell commit immediately and the data stream in, rather than reading a claim. Identical before and after means the fix did nothing — roll it back.
  • Present a run as a list of results the user can click through — one line per navigation: the route, what commits instantly, and what streams in — not a transcript of the loop.
  • Only surface a question for a genuine fork: a fix that would change behavior, a security-sensitive read, or a route that's dynamic by design (a per-link-prefetch candidate, not a shell to grow). A clean instant fix is not a fork — keep going. With no one to ask (an unattended run), don't block: take the safe default and note the assumption — for a cache-freshness choice, defer the read behind <Suspense> (always fresh, still instant) rather than guess a cacheLife.

The workflow

- [ ] P  PREREQS      Next.js 16.3+ with cacheComponents: true; upgrade first → below
- [ ] 0  SETUP        once per repo: discover + write instant-nav.rig.md     → rig-template.md
- [ ] A  RIG          production build with the testing API exposed          → below
- [ ] B  BASELINE     unlocked: the marker renders for the test user         → test-template.md
- [ ] C  RED          locked instant(): the shell does not commit            → test-template.md
- [ ] C-gate          VERIFY-RED: stop until the RED is trustworthy          → reference/red-test-robustness.md
- [ ] D  FIX          push each Suspense boundary down to the data it guards → reference/patterns.md
- [ ]      D1 reuse the route's existing loading UI; do not hand-build skeletons
- [ ]      D2 the shell matches the real render at every breakpoint  → reference/real-app-patterns.md
- [ ] E  PARITY       the refactor changed only whether the route is instant
- [ ] F  DIFFERENTIAL revert only the fix → RED; re-apply → GREEN            → reference/red-test-robustness.md
- [ ] G  REVIEW       PR checklist (below)

Phases B and C build the test; only the locked test from C ships.


P. PREREQUISITES: current Next.js with Cache Components

The workflow depends on framework capabilities that ship with current Next.js:

  • Next.js 16.3+ with `cacheComponents: true` in next.config.ts. Without Cache Components there is no static shell to optimize.
  • `@next/playwright` on the same release line as the project's next; it provides instant(). Verify with npm ls next @next/playwright (or the project's package manager) and align them if they differ. The matching testing API is in the next runtime, gated by the experimental.exposeTestingApiInProductionBuild config flag (phase A).

If the project does not meet these, upgrade first (npx @next/codemod upgrade automates most of it), then enable Cache Components in next.config.ts:

export default { cacheComponents: true }

Enabling the flag surfaces the blocking routes to resolve first; the `next-cache-components-adoption` skill drives that adoption. Reach for this optimizer once the app builds under Cache Components.

This gate is deliberate: the skill targets current Next.js, and none of the verdicts below are meaningful on older versions.

0. SETUP: discover this project's rig, once per repo

The principles in this skill are fixed; the infrastructure they run on is