Capítulo 60 de 456
Migration guide from route segment configs (dynamic, revalidate, fetchCache) to Cache Components (use cache + cacheLife), driven by instant-navigation validation that flags routes blocking instant navigation in dev.
cacheComponents: true): Next.js 16 config flag; replaces experimental.dynamicIO/experimental.useCache. All routes are dynamic by default; caching is opt-in via use cache.instant route segment config: instant = false marks a segment as allowed to block navigation, letting you defer fixing it (opt-out) without forcing dynamic rendering — a genuinely prerenderable route still ships a static shell.next-cache-components-adoption skill: official coding-agent skill (npx skills add vercel/next.js --skill next-cache-components-adoption) that automates the migration route by route, with incremental or direct modes.cache-components-instant-false codemod: bulk-adds instant = false to every page/layout/default that doesn't declare it, to opt the whole app out of validation at once.new Date(), Date.now(), Math.random(), crypto.randomUUID() during prerender throw a build error even with instant = false; must be moved behind connection() + <Suspense> or into a Client Component.updateTag vs revalidateTag: updateTag (Server Actions only) expires a tag so the next request blocks for fresh data (read-your-own-writes); revalidateTag requires a cache profile argument (e.g. 'max') and serves stale-while-revalidate.<Activity> in "hidden" mode instead of unmounting — useState, form inputs, scroll position persist across navigation.// Before: fetch() cache options
export default async function Page() {
const res = await fetch('https://api.example.com/data', {
cache: 'force-cache',
next: { revalidate: 3600, tags: ['data'] },
})
const data = await res.json()
return <div>...</div>
}
// After: use cache directive
import { cacheLife, cacheTag } from 'next/cache'
async function getData() {
'use cache'
cacheLife('hours')
cacheTag('data')
const res = await fetch('https://api.example.com/data')
return res.json()
}
export default async function Page() {
const data = await getData()
return <div>...</div>
}
cache/next.revalidate/next.tags do fetch() viram cacheLife/cacheTag dentro de uma função 'use cache'.// Wrapping runtime data access (cookies/headers) in Suspense
import { cookies } from 'next/headers'
import { Suspense } from 'react'
export default function Page() {
return (
<Suspense fallback={<p>Loading...</p>}>
<Dashboard />
</Suspense>
)
}
async function Dashboard() {
const theme = (await cookies()).get('theme')?.value
// ...
}
cookies()/headers() precisa estar isolado num componente dentro de <Suspense> para o resto da página prerenderizar como shell estático.| Config antigo | Substituto em Cache Components |
|---|---|
dynamic = 'force-dynamic' | remover (dinâmico é o padrão) |
dynamic = 'force-static' | use cache com cacheLife('max') |
revalidate = N | cacheLife(...) dentro de use cache |
fetchCache | remover (automático dentro de use cache) |
unstable_cache | função com 'use cache' + cacheLife/cacheTag |
unstable_noStore | remover (não-cacheado é o padrão) |
dynamicParams | remover; usar notFound() na página |
runtime = 'edge' | remover; usar Proxy para comportamento edge |
experimental_ppr | remover; cacheComponents já inclui Partial Prerendering |
[] em generateStaticParams: agora gera erro (empty-generate-static-params); precisa retornar ao menos um param.await params no topo do componente: bloqueia o shell estático; passar a promise para dentro de um componente <Suspense>.try/catch de Route Handlers: acesso a dado não-cacheado bail-a via throw, poluindo o build log; usar experimental.hideLogsAfterAbort: true.updateTag fora de Server Action: lança erro; para Route Handlers/webhooks usar revalidateTag com profile.cacheComponents: true em next.config.ts.instant = false opta rotas fora da validação para converter uma de cada vez, mas não livra de erros de IO síncrono.use cache por padrão é in-memory (não sobrevive a redeploy/instância nova); usar use cache: remote ou cache handler para persistência durável.generateMetadata/generateViewport seguem as mesmas regras de componentes: cachear dado externo com use cache, ou isolar dado runtime com um marcador dinâmico (não dá pra envolver generateMetadata em <Suspense> diretamente).<Activity> pode exigir lógica de reset explícita (dropdowns, diálogos, forms) que antes vinha "de graça" pelo unmount.