Next.js Cache Components Expert
Оптимизируйте приложения Next.js с помощью Cache Components и PPR. Получите экспертные подсказки ИИ для паттернов 'use cache', cacheLife и Partial Prerendering в Claude Code.
Этот навык предоставляет специализированные рекомендации по внедрению Next.js Cache Components и шаблонов Partial Prerendering (PPR), отходя от устаревших конфигураций сегментов к гранулированному кэшированию на уровне кода. Он активно помогает разработчикам применять директиву 'use cache', управлять временем жизни кэша с помощью cacheLife и реализовывать надежные стратегии инвалидации с cacheTag и updateTag. Анализируя конфигурации проектов, он обеспечивает оптимальную производительность за счет стратегического разделения статических оболочек и динамического потокового контента, помогая разработчикам ориентироваться в последних архитектурных шаблонах Next.js с помощью обратной связи во время сборки и лучших практик.
Ключевые особенности
| name | description |
|---|---|
cache-components |
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR).
**PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations.
**DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions.
**USE CASES**: 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, debugging cache issues, and reviewing Cache Component implementations.
|
Auto-activation: This skill activates automatically in projects with
cacheComponents: truein next.config.
When starting work in a Next.js project, check if Cache Components are enabled:
# Check next.config.ts or next.config.js for cacheComponents
grep -r "cacheComponents" next.config.* 2>/dev/nullIf cacheComponents: true is found, apply this skill's patterns proactively when:
- Writing React Server Components
- Implementing data fetching
- Creating Server Actions with mutations
- Optimizing page performance
- Reviewing existing component code
Cache Components enable Partial Prerendering (PPR) - mixing static HTML shells with dynamic streaming content for optimal performance.
Cache Components represents a shift from segment configuration to compositional code:
| Before (Deprecated) | After (Cache Components) |
|---|---|
export const revalidate = 3600 |
cacheLife('hours') inside 'use cache' |
export const dynamic = 'force-static' |
Use 'use cache' and Suspense boundaries |
| All-or-nothing static/dynamic | Granular: static shell + cached + dynamic |
Key Principle: Components co-locate their caching, not just their data. Next.js provides build-time feedback to guide you toward optimal patterns.
┌─────────────────────────────────────────────────────┐
│ Static Shell │
│ (Sent immediately to browser) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Header │ │ Cached │ │ Suspense │ │
│ │ (static) │ │ Content │ │ Fallback │ │
│ └─────────────┘ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ Dynamic │ │
│ │ (streams) │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────┘
When writing a React Server Component, ask these questions in order:
┌─────────────────────────────────────────────────────────┐
│ Does this component fetch data or perform I/O? │
└─────────────────────┬───────────────────────────────────┘
│
┌──────────▼──────────┐
│ YES │ NO → Pure component, no action needed
└──────────┬──────────┘
│
┌─────────────────▼─────────────────┐
│ Does it depend on request context? │
│ (cookies, headers, searchParams) │
└─────────────────┬─────────────────┘
│
┌────────────┴────────────┐
│ │
┌────▼────┐ ┌─────▼─────┐
│ YES │ │ NO │
└────┬────┘ └─────┬─────┘
│ │
│ ┌─────▼─────────────────┐
│ │ Can this be cached? │
│ │ (same for all users?) │
│ └─────┬─────────────────┘
│ │
│ ┌──────────┴──────────┐
│ │ │
│ ┌────▼────┐ ┌─────▼─────┐
│ │ YES │ │ NO │
│ └────┬────┘ └─────┬─────┘
│ │ │
│ ▼ │
│ 'use cache' │
│ + cacheTag() │
│ + cacheLife() │
│ │
└──────────────┬─────────────────────┘
│
▼
Wrap in <Suspense>
(dynamic streaming)
Key insight: The 'use cache' directive is for data that's the same across users. User-specific data stays dynamic with Suspense.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig// Cached component - output included in static shell
async function CachedPosts() {
'use cache'
const posts = await db.posts.findMany()
return <PostList posts={posts} />
}
// Page with static + cached + dynamic content
export default async function BlogPage() {
return (
<>
<Header /> {/* Static */}
<CachedPosts /> {/* Cached */}
<Suspense fallback={<Skeleton />}>
<DynamicComments /> {/* Dynamic - streams */}
</Suspense>
</>
)
}Marks code as cacheable. Can be applied at three levels:
// File-level: All exports are cached
'use cache'
export async function getData() {
/* ... */
}
export async function Component() {
/* ... */
}
// Component-level
async function UserCard({ id }: { id: string }) {
'use cache'
const user = await fetchUser(id)
return <Card>{user.name}</Card>
}
// Function-level
async function fetchWithCache(url: string) {
'use cache'
return fetch(url).then((r) => r.json())
}Important: All cached functions must be async.
import { cacheLife } from 'next/cache'
async function Posts() {
'use cache'
cacheLife('hours') // Use a predefined profile
// Or custom configuration:
cacheLife({
stale: 60, // 1 min - client cache validity
revalidate: 3600, // 1 hr - start background refresh
expire: 86400, // 1 day - absolute expiration
})
return await db.posts.findMany()
}Predefined profiles: 'default', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'max'
import { cacheTag } from 'next/cache'
async function BlogPosts() {
'use cache'
cacheTag('posts')
cacheLife('days')
return await db.posts.findMany()
}
async function UserProfile({ userId }: { userId: string }) {
'use cache'
cacheTag('users', `user-${userId}`) // Multiple tags
return await db.users.findUnique({ where: { id: userId } })
}For read-your-own-writes semantics:
'use server'
import { updateTag } from 'next/cache'
export async function createPost(formData: FormData) {
await db.posts.create({ data: formData })
updateTag('posts') // Client immediately sees fresh data
}For stale-while-revalidate pattern:
'use server'
import { revalidateTag } from 'next/cache'
export async function updatePost(id: string, data: FormData) {
await db.posts.update({ where: { id }, data })
revalidateTag('posts', 'max') // Serve stale, refresh in background
}| Content Type | API | Behavior |
|---|---|---|
| Static | No directive | Rendered at build time |
| Cached | 'use cache' |
Included in static shell, revalidates |
| Dynamic | Inside <Suspense> |
Streams at request time |
Critical Concept: With Cache Components, Next.js renders ALL permutations of provided parameters to create reusable subshells.
// app/products/[category]/[slug]/page.tsx
export async function generateStaticParams() {
return [
{ category: 'jackets', slug: 'classic-bomber' },
{ category: 'jackets', slug: 'essential-windbreaker' },
{ category: 'accessories', slug: 'thermal-fleece-gloves' },
]
}Next.js renders these routes:
/products/jackets/classic-bomber ← Full params (complete page)
/products/jackets/essential-windbreaker ← Full params (complete page)
/products/accessories/thermal-fleece-gloves ← Full params (complete page)
/products/jackets/[slug] ← Partial params (category subshell)
/products/accessories/[slug] ← Partial params (category subshell)
/products/[category]/[slug] ← No params (fallback shell)
Why this matters: The category subshell (/products/jackets/[slug]) can be reused for ANY jacket product, even ones not in generateStaticParams. Users navigating to an unlisted jacket get the cached category shell immediately, with product details streaming in.
With Cache Components enabled:
- Must provide at least one parameter - Empty arrays now cause build errors (prevents silent production failures)
- Params prove static safety - Providing params lets Next.js verify no dynamic APIs are called
- Partial params create subshells - Each unique permutation generates a reusable shell
// ❌ ERROR with Cache Components
export function generateStaticParams() {
return [] // Build error: must provide at least one param
}
// ✅ CORRECT: Provide real params
export async function generateStaticParams() {
const products = await getPopularProducts()
return products.map(({ category, slug }) => ({ category, slug }))
}Arguments become part of the cache key:
// Different userId = different cache entry
async function UserData({ userId }: { userId: string }) {
'use cache'
cacheTag(`user-${userId}`)
return await fetchUser(userId)
}Cache Components provides early feedback during development. These build errors guide you toward optimal patterns:
Error: Accessing cookies/headers/searchParams outside a Suspense boundary
Solution: Wrap dynamic components in <Suspense>:
<Suspense fallback={<Skeleton />}>
<ComponentThatUsesCookies />
</Suspense>Error: Accessing uncached data outside Suspense
Solution: Either cache the data or wrap in Suspense:
// Option 1: Cache it
async function ProductData({ id }: { id: string }) {
'use cache'
return await db.products.findUnique({ where: { id } })
}
// Option 2: Make it dynamic with Suspense
;<Suspense fallback={<Loading />}>
<DynamicProductData id={id} />
</Suspense>Error: Cannot access cookies/headers inside 'use cache'
Solution: Extract runtime data outside cache boundary (see "Handling Runtime Data" above).
- For complete API reference, see REFERENCE.md
- For common patterns and recipes, see PATTERNS.md
- For debugging and troubleshooting, see TROUBLESHOOTING.md
When generating Cache Component code:
- Always use
async- All cached functions must be async - Place
'use cache'first - Must be first statement in function body - Call
cacheLife()early - Should follow'use cache'directive - Tag meaningfully - Use semantic tags that match your invalidation needs
- Extract runtime data - Move
cookies()/headers()outside cached scope - Wrap dynamic content - Use
<Suspense>for non-cached async components
When cacheComponents: true is detected in the project, automatically apply these patterns:
Ask yourself: "Can this data be cached?" If yes, add 'use cache':
// Before: Uncached fetch
async function ProductList() {
const products = await db.products.findMany()
return <Grid products={products} />
}
// After: With caching
async function ProductList() {
'use cache'
cacheTag('products')
cacheLife('hours')
const products = await db.products.findMany()
return <Grid products={products} />
}Always invalidate relevant caches after mutations:
'use server'
import { updateTag } from 'next/cache'
export async function createProduct(data: FormData) {
await db.products.create({ data })
updateTag('products') // Don't forget!
}Structure with static shell + cached content + dynamic streaming:
export default async function Page() {
return (
<>
<StaticHeader /> {/* No cache needed */}
<CachedContent /> {/* 'use cache' */}
<Suspense fallback={<Skeleton />}>
<DynamicUserContent /> {/* Streams at runtime */}
</Suspense>
</>
)
}Flag these issues in Cache Components projects:
- Data fetching without
'use cache'where caching would benefit - Missing
cacheTag()calls (makes invalidation impossible) - Missing
cacheLife()(relies on defaults which may not be appropriate) - Server Actions without
updateTag()/revalidateTag()after mutations -
cookies()/headers()called inside'use cache'scope - Dynamic components without
<Suspense>boundaries - DEPRECATED:
export const revalidate- replace withcacheLife()in'use cache' - DEPRECATED:
export const dynamic- replace with Suspense + cache boundaries - Empty
generateStaticParams()return - must provide at least one param
🧠 Что это
Next.js Cache Components Expert — это экспертная подсказка (skill) для разработчиков Next.js, проекты которых используют новую экспериментальную систему Cache Components и Partial Prerendering (PPR). Skill автоматически активируется, как только в next.config.ts / next.config.js обнаруживает cacheComponents: true.
Основная идея этой системы — Code Over Configuration: вместо устаревших export const revalidate или export const dynamic весь контроль над кэшированием переносится в тело компонента или функции с помощью директив 'use cache', cacheLife() и cacheTag(). Это позволяет строить страницы из «статической оболочки» (отдаётся мгновенно), кэшированного контента (фоновое обновление) и динамических секций (стриминг через Suspense). Skill подсказывает, как применять эти паттерны, проверять код на соответствие и избегать типовых ошибок.
⚙️ Как работает
🔍 Авто-активация и детекция проекта
При старте сессии в Next.js-проекте skill выполняет проверку:
grep -r "cacheComponents" next.config.*— ищет флаг в конфиге.- Если
cacheComponents: trueнайден, skill автоматически применяет свои рекомендации к каждому компоненту, запросу данных, Server Action и коду на ревью.
🧭 Ментальная модель: дерево решений для кэширования
Для каждого React Server Component задаётся цепочка вопросов:
- Компонент делает I/O (запрос к БД, fetch)? — Нет → чистая отрисовка, без действий. Да → дальше.
- Зависит от контекста запроса (
cookies,headers,searchParams)? — Да → динамика (стриминг внутриSuspense). Нет → дальше. - Данные одинаковы для всех пользователей? — Нет → динамический
Suspense. Да → можно кэшировать.
Если данные можно кэшировать — компонент получает:
- Директиву
'use cache' cacheTag()для последующей инвалидацииcacheLife()для указания времени жизни
🧩 Core APIs
| API | Назначение | Пример |
|---|---|---|
'use cache' |
Директива на уровне функции, компонента или всего файла. Все функции внутри становятся кэшируемыми (обязательно async). |
async function Posts() { 'use cache' … } |
cacheLife(profile) |
Управление временем жизни кэша. Можно использовать предопределённые профили ('seconds', 'minutes', 'hours', 'days', 'weeks', 'max') или задать свои { stale, revalidate, expire }. |
cacheLife('hours') или cacheLife({ stale: 60, revalidate: 3600 }) |
cacheTag(...tags) |
Навешивание тегов на кэшированный кусок. Теги — строки, могут быть динамическими (cacheTag('user', user-${userId})). |
cacheTag('posts') |
updateTag(tag) |
Мгновенная инвалидация кэша — после мутации данных клиент сразу видит свежие данные (pattern Read-Your-Own-Writes). | updateTag('posts') |
revalidateTag(tag, profile?) |
Фоновая ревалидация — пока старый кэш отдаётся, новый начинает обновляться (stale-while-revalidate). | revalidateTag('posts', 'max') |
📦 Параметры и Subshels
Ключевая особенность: Next.js рендерит все комбинации параметров из generateStaticParams, создавая «субшеллы» (subshells).
Например, для app/products/[category]/[slug]/page.tsx:
/products/jackets/classic-bomber— полная страница (все параметры известны)./products/jackets/[slug]— категорийный субшелл. Он может быть отдан мгновенно для любого товара категории «jackets», даже если его нет вgenerateStaticParams. Только динамическая часть (например, описание) будет достримирована.
Важно: с Cache Components generateStaticParams не может возвращать пустой массив — это вызовет ошибку сборки. Нужно предоставить хотя бы один реальный параметр (например, самые популярные товары).
⏳ Build-Time Feedback
Сборка выдаёт понятные ошибки, которые направляют разработчика:
- «Dynamic data outside Suspense» → оберни динамический компонент в
Suspense. - «Uncached data outside Suspense» → либо добавь
'use cache', либо оберни вSuspense. - «Request data inside cache» — вынеси
cookies()/headers()за пределы скоупа с'use cache'.
🎯 Когда использовать
Этот skill незаменим в проектах, где:
- Включён
cacheComponents: trueв конфиге Next.js. - Используется Partial Prerendering (PPR) — смешивание статики и динамики на одной странице.
- Требуется тонкая настройка кэширования (разное время жизни для разных блоков данных).
- Нужна инвалидация кэша с гарантией, что пользователь видит свои только что созданные/обновлённые данные.
- Проводится code review — skill автоматически подсвечивает устаревшие паттерны (
export const revalidate,export const dynamic), отсутствиеcacheTag()или пустойgenerateStaticParams.
Skill будет полезен как разработчикам, которые только начинают внедрять Cache Components — через дерево решений и чёткие примеры, — так и опытным инженерам, которые хотят автоматизировать проверки и ускорить разработку.
❗ Важно знать
- Ключ кэша = аргументы функции. Разные значения
userIdсоздают разные записи в кэше, тэгированные соответствующим образом. 'use cache'должна быть первой строкой тела функции. Сразу за ней рекомендуется вызыватьcacheLife()иcacheTag().- Не путай
updateTagиrevalidateTag: первый даёт клиенту свежие данные немедленно (идеально для optimistic updates), второй — отдаёт устаревшее из кэша и параллельно обновляет его в фоне (подходит для не критичных к задержкам данных). - Всегда проверяй код на ревью: skill предлагает чек-лист: нет ли запросов данных без
'use cache', не забыты лиcacheTag/cacheLife, не попали лиcookies()внутрь кэшированного скоупа, не используется ли устаревшийexport const revalidate.
💻 Пример быстрой конфигурации
# next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
🧪 Пример компонента с кэшированием
async function BlogPosts() {
'use cache'
cacheTag('posts')
cacheLife('hours')
const posts = await db.posts.findMany()
return <PostList posts={posts} />
}
После мутации (создания поста) вызываем updateTag('posts') в Server Action — и клиент мгновенно получает свежий список, не перезагружая страницу.
Как активируется навык cache-components?
Навык активируется автоматически, когда Claude обнаруживает 'cacheComponents: true' в файле next.config.js или next.config.ts вашего проекта.
Как этот навык обрабатывает Partial Prerendering (PPR)?
Он предоставляет мысленную модель для разделения статической оболочки и динамического потокового контента, гарантируя, что динамические данные корректно обёрнуты в границы Suspense для оптимальной производительности.
Могу ли я использовать этот навык для отладки ошибок кэширования Next.js?
Да, он предназначен для распознавания и предоставления решений распространённых ошибок времени сборки, таких как доступ к cookies/headers внутри блока 'use cache' или доступ к некэшированным данным вне Suspense.
В чём основное преимущество Cache Components перед segment config?
Cache Components позволяют использовать 'Code Over Configuration', то есть логика кэширования размещается внутри компонентов с помощью директивы 'use cache', а не применяется ко всему маршруту.
Синхронизируйте навыки с Claude Cowork, Claude Code, Codex и другими.
Установите одной командой.
npx skillfish add vercel/next.js cache-componentsИсточник: https://mcpmarket.com/tools/skills/next-js-cache-components-expert
Комментарии
Комментариев пока нет. Будьте первым.