Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build correct cached/dynamic boundaries in the Next.js App Router when Cache Components or PPR are enabled. Use when working with cacheComponents, Partial Prerendering (PPR), 'use cache', cacheLife, cacheTag, updateTag, or revalidateTag, to avoid request-context leaks and enforce proper cache invalidation. Not for the Pages Router or when Cache Components/PPR are off.
.claude/skills/asymmetric-al-cache-components/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -22% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -28% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 2% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 14% | 0% |
Name: cache-components Purpose: Build correct cached/dynamic boundaries in Next.js App Router when Cache Components or PPR are in use. Use this skill to avoid request-context leaks and to enforce proper cache invalidation.
Applies when: cacheComponents: true, Partial Prerendering (PPR), 'use cache', cacheLife, cacheTag, updateTag, revalidateTag. Do not use when: Working in the Pages Router or when Cache Components/PPR are not enabled.
<Suspense>.cookies(), headers(), or auth/session inside a 'use cache' scope.'use cache' function/component must be async.'use cache', cacheLife, cacheTag over route-segment config (revalidate, dynamic).updateTag for immediate consistency or revalidateTag for background refresh.partialPrefetching: true, Next.js prefetches one reusable shell per route. Every server await is a Stream (<Suspense>) / Cache ('use cache') / Block (export const instant = false + reason comment) decision; Instant Insights errors link the canonical fix at nextjs.org/docs/messages/... — apply that pattern, don't improvise.<Link prefetch={true}> extends prefetching to build-time-known cached content on that link; export const prefetch = 'allow-runtime' extends it to request-time cached content and requires PR justification.'use cache' + cacheTag (and cacheLife if needed).<Suspense>.'use cache'cacheTag<Suspense>asyncexport const instant = false with a reasonts"use cache"; import { cacheLife, cacheTag } from "next/cache"; export async function getProducts(category: string) { cacheLife("minutes"); cacheTag("products"); cacheTag(`products:${category}`); }
tsximport { Suspense } from "react"; export default function Page() { return ( <> <MainCached /> <Suspense fallback={null}> <UserPanel /> </Suspense> </> ); }
ts"use server"; import { updateTag } from "next/cache"; export async function updateProduct(id: string) { updateTag(`product:${id}`); updateTag("products"); }
'use cache'<Suspense>generateStaticParams| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | pass→pass | 16,663 | 12,058 | -28% | 1 | 1 | 0% | 2,707 | 3,120 | +15% | 0 | 0 | — |
case-01 | fail→pass | 15,237 | 6,459 | -58% | 1 | 1 | 0% | 2,713 | 2,108 | -22% | 0 | 0 | — |
case-02 | fail→pass | 14,480 | 10,012 | -31% | 1 | 1 | 0% | 2,488 | 2,639 | +6% | 0 | 0 | — |
case-03 | pass→pass | 17,969 | 7,858 | -56% | 1 | 1 | 0% | 3,166 | 2,356 | -26% | 0 | 0 | — |
case-04 | pass→pass | 11,755 | 7,296 | -38% | 1 | 1 | 0% | 1,969 | 2,143 | +9% | 0 | 0 | — |
case-06 | pass→pass | 11,443 | 8,058 | -30% | 1 | 1 | 0% | 1,883 | 2,242 | +19% | 0 | 0 | — |
case-07 | pass→pass | 12,873 | 10,261 | -20% | 1 | 1 | 0% | 2,097 | 2,824 | +35% | 0 | 0 | — |
case-08 | pass→pass | 15,034 | 9,211 | -39% | 1 | 1 | 0% | 2,316 | 2,663 | +15% | 0 | 0 | — |
case-09 | pass→pass | 10,723 | 9,917 | -8% | 1 | 1 | 0% | 1,809 | 2,620 | +45% | 0 | 0 | — |
case-18 | pass→pass | 14,268 | 6,812 | -52% | 1 | 1 | 0% | 2,307 | 2,001 | -13% | 0 | 0 | — |
case-10 | fail→pass | 13,918 | 4,272 | -69% | 1 | 1 | 0% | 2,344 | 1,681 | -28% | 0 | 0 | — |
case-11 | fail→pass | 8,529 | 3,231 | -62% | 1 | 1 | 0% | 1,436 | 1,458 | +2% | 0 | 0 | — |
case-12 | pass→pass | 10,455 | 3,908 | -63% | 1 | 1 | 0% | 1,693 | 1,535 | -9% | 0 | 0 | — |
case-13 | fail→pass | 10,643 | 7,107 | -33% | 1 | 1 | 0% | 1,975 | 2,242 | +14% | 0 | 0 | — |
case-14 | pass→pass | 8,472 | 5,460 | -36% | 1 | 1 | 0% | 1,511 | 1,913 | +27% | 0 | 0 | — |
case-15 | pass→pass | 13,485 | 8,823 | -35% | 1 | 1 | 0% | 2,130 | 2,450 | +15% | 0 | 0 | — |
case-16 | pass→pass | 12,534 | 7,072 | -44% | 1 | 1 | 0% | 2,230 | 2,165 | -3% | 0 | 0 | — |
case-17 | pass→pass | 13,634 | 8,101 | -41% | 1 | 1 | 0% | 2,265 | 2,305 | +2% | 0 | 0 | — |
case-19 | pass→pass | 12,001 | 4,051 | -66% | 1 | 1 | 0% | 1,930 | 1,533 | -21% | 0 | 0 | — |
case-20 | pass→pass | 12,373 | 6,087 | -51% | 1 | 1 | 0% | 1,987 | 1,955 | -2% | 0 | 0 | — |
case-21 | fail→pass | 15,564 | 9,825 | -37% | 1 | 1 | 0% | 2,479 | 2,669 | +8% | 0 | 0 | — |
case-22 | pass→pass | 6,303 | 2,625 | -58% | 1 | 1 | 0% | 984 | 1,300 | +32% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.