spinny:~/writing $ vim nextjs-16-cache-components.md
1~2Per anni una delle domande più fastidiose in Next.js è stata: "Questa pagina è statica o dinamica?". Sembra una domanda semplice, finché non aggiungi una chiamata a `cookies()`, un `fetch` con opzioni diverse, un client database, un CMS, un carrello o un pezzetto di contenuto personalizzato.3~4Next.js 16 è interessante perché prova a rendere questa conversazione meno misteriosa. Non elimina la complessità, ma sposta il modello mentale: le route sono dinamiche di default, la cache si dichiara dove serve, e `Suspense` diventa il modo naturale per comporre shell veloci con parti che restano fresche.5~6La feature da capire è Cache Components. Turbopack stabile, React Compiler, `proxy.ts` e le nuove API di invalidazione sono importanti, ma girano attorno allo stesso problema: costruire app veloci senza dover indovinare cosa il framework ha deciso dietro le quinte.7~8## Perché questa cosa conta9~10In un'app reale non hai solo "pagine statiche" e "pagine dinamiche". Hai pezzi diversi con esigenze diverse.11~12La scheda prodotto può cambiare poche volte al giorno. Il prezzo magari cambia più spesso. La disponibilità deve essere quasi live. Il nome utente è personale. Le recensioni possono arrivare in streaming. La sidebar può essere stabile. Il carrello no.13~14Se tratti tutto come una sola unità, finisci sempre in uno dei due estremi:15~16- cache aggressiva e rischio di vedere dati vecchi;17- rendering dinamico ovunque e performance peggiore del necessario.18~19Cache Components serve proprio a evitare questa scelta finta.20~21## Il modello in pratica22~23Con `cacheComponents: true`, puoi dichiarare cosa è cacheabile usando `"use cache"`. Poi puoi associare durata e tag con `cacheLife()` e `cacheTag()`. Le parti dinamiche restano dinamiche e possono essere isolate con `Suspense`.24~25```mermaid26flowchart TD27 Request[Richiesta utente] --> Shell[Shell cacheata]28 Request --> Dynamic[Sezioni dinamiche]29 Shell --> FirstPaint[Primo contenuto veloce]30 Dynamic --> Stream[Streaming dentro Suspense]31 Stream --> Complete[Pagina completa]32```33~34La configurazione è piccola:35~36```typescript37// next.config.ts38import type { NextConfig } from 'next';39~40const nextConfig: NextConfig = {41 cacheComponents: true,42};43~44export default nextConfig;45```46~47Il cambio grosso non è nella config. È nel modo in cui inizi a scrivere i componenti.48~49```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';53~54async function getProduct(slug: string) {55 'use cache';56~57 cacheLife('hours');58 cacheTag(`product:${slug}`);59~60 return db.product.findUnique({ where: { slug } });61}62~63async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);65~66 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}73~74async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}78~79export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;81~82 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```92~93La pagina non deve essere tutta cacheata o tutta dinamica. La scheda prodotto può essere veloce e riusabile. L'inventario può restare fresco. L'utente vede qualcosa subito, senza aspettare la parte più lenta.94~95## `use cache` è documentazione eseguibile96~97La cosa che mi piace di `"use cache"` è che obbliga a rendere esplicita un'intenzione. Quando leggi una funzione, capisci subito che qualcuno ha deciso: "questo dato può essere riusato".98~99È particolarmente utile quando non stai usando `fetch`. Tante app leggono dati da Prisma, Drizzle, SDK interni, client CMS o funzioni di servizio. In quei casi il vecchio ragionamento basato solo sulle opzioni di `fetch` non bastava.100~101Una regola pratica:102~103- cachea contenuto relativamente stabile;104- usa tag granulari;105- lascia dinamici permessi, sessioni, carrelli, notifiche e stati transazionali;106- metti le parti lente dentro `Suspense`;107- misura prima di dire "abbiamo migliorato la performance".108~109## Invalidare senza buttare via tutto110~111La cache è utile solo se puoi aggiornarla con precisione. Qui `cacheTag`, `revalidateTag` e `updateTag` diventano importanti.112~113Esempio:114~115```typescript116'use server';117~118import { updateTag } from 'next/cache';119~120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125~126 updateTag(`product:${productId}`);127}128```129~130Il dettaglio importante è il tag. `product:${productId}` racconta un confine preciso. `products` racconta un secchio enorme. All'inizio il secchio enorme è comodo; dopo qualche mese diventa il motivo per cui invalidi mezza app per cambiare un titolo.131~132## Turbopack stabile: la parte che senti ogni giorno133~134Next.js 16 porta Turbopack al centro per sviluppo e build. Non è la feature più poetica, ma è quella che senti mentre lavori: server che parte prima, refresh più rapido, build che smettono di sembrare una pausa caffè forzata.135~136Detto questo, non migrerei una codebase piena di plugin custom a occhi chiusi. Controllerei:137~138- build locale;139- moduli non standard;140- MDX, SVG e CSS;141- plugin Webpack rimasti;142- pagine critiche;143- differenze nei tempi di build.144~145Per i progetti nuovi, partirei dal default. Per quelli maturi, farei una migrazione misurata.146~147## React Compiler: togliere rumore, non pensiero148~149React Compiler 1.0 è stabile e Next.js 16 lo supporta con `reactCompiler`. La promessa è ridurre molta memoization manuale: meno `memo`, meno `useMemo`, meno `useCallback` usati "per sicurezza".150~151```typescript152// next.config.ts153import type { NextConfig } from 'next';154~155const nextConfig: NextConfig = {156 reactCompiler: true,157};158~159export default nextConfig;160```161~162Non lo tratterei come una polvere magica. Il compiler aiuta quando il codice segue bene le regole di React. Se i componenti hanno side effect strani, mutazioni nascoste o hook usati male, prima va sistemato quello.163~164Il modo sano di provarlo:165~1661. aggiorna `eslint-plugin-react-hooks`;1672. correggi le violazioni reali;1683. abilitalo su un'area controllata;1694. misura build time e comportamento;1705. rimuovi memoization manuale solo quando non serve più.171~172L'obiettivo non è cancellare ogni `useMemo`. L'obiettivo è smettere di scrivere memoization preventiva perché abbiamo paura del render.173~174## `proxy.ts` e il confine di rete175~176Il vecchio `middleware.ts` diventa `proxy.ts`. È un cambio di nome, ma ha senso: quel file sta al confine della richiesta, non è un middleware generico in stile backend tradizionale.177~178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181~182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184~185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188~189 return NextResponse.next();190}191```192~193La regola qui è semplice: tienilo piccolo. Redirect, auth routing, header, rewrites essenziali. Se inizia a sembrare un secondo backend, probabilmente sta facendo troppo.194~195## Come migrerei davvero196~197Non accenderei tutte le feature insieme. Farei così:198~1991. aggiorno Next, React e React DOM;2002. lancio il codemod ufficiale;2013. sistemo breaking change su `params`, `searchParams`, `cookies()`, `headers()` e `draftMode()`;2024. migro `middleware.ts` a `proxy.ts`;2035. verifico build e pagine critiche;2046. abilito Cache Components su una sezione dove oggi la cache crea attrito;2057. definisco convenzioni per tag e invalidazione;2068. provo React Compiler separatamente;2079. confronto metriche prima e dopo.208~209La migrazione buona non è quella che usa tutte le feature nuove. È quella che rende il comportamento dell'app più leggibile.210~211## Cosa cambia nel modo di pensare212~213La cosa più utile di Next.js 16 è che ti costringe a nominare meglio le intenzioni. Una funzione non è solo "prende il prodotto dal database". È "prende il prodotto, lo posso cacheare per ore, lo invalido con questo tag". Un componente non è solo "renderizza la pagina". È "questa è la shell veloce, questo pezzo è personale, questo arriva in streaming".214~215All'inizio sembra più lavoro. Poi diventa una forma di calma. Le decisioni di performance non stanno più nascoste in una combinazione di default, euristiche e memoria tribale. Stanno nel codice.216~217## Fonti utili218~219- [Next.js 16 release notes](https://nextjs.org/blog/next-16)220- [Cache Components - Next.js docs](https://nextjs.org/docs/app/getting-started/cache-components)221- [use cache - Next.js docs](https://nextjs.org/docs/app/api-reference/directives/use-cache)222- [React Compiler v1.0](https://react.dev/blog/2025/10/07/react-compiler-1)223~
NORMAL · nextjs-16-cache-components.md [readonly]223 lines · :q to close