spinny:~/writing $ less nextjs-16-cache-components.md
12Per 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.34Next.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.56La 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.78## Perché questa cosa conta910In un'app reale non hai solo "pagine statiche" e "pagine dinamiche". Hai pezzi diversi con esigenze diverse.1112La 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.1314Se tratti tutto come una sola unità, finisci sempre in uno dei due estremi:1516- cache aggressiva e rischio di vedere dati vecchi;17- rendering dinamico ovunque e performance peggiore del necessario.1819Cache Components serve proprio a evitare questa scelta finta.2021## Il modello in pratica2223Con `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`.2425```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```3334La configurazione è piccola:3536```typescript37// next.config.ts38import type { NextConfig } from 'next';3940const nextConfig: NextConfig = {41 cacheComponents: true,42};4344export default nextConfig;45```4647Il cambio grosso non è nella config. È nel modo in cui inizi a scrivere i componenti.4849```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';5354async function getProduct(slug: string) {55 'use cache';5657 cacheLife('hours');58 cacheTag(`product:${slug}`);5960 return db.product.findUnique({ where: { slug } });61}6263async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);6566 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}7374async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}7879export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;8182 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```9293La 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.9495## `use cache` è documentazione eseguibile9697La 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".9899È 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.100101Una regola pratica:102103- 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".108109## Invalidare senza buttare via tutto110111La cache è utile solo se puoi aggiornarla con precisione. Qui `cacheTag`, `revalidateTag` e `updateTag` diventano importanti.112113Esempio:114115```typescript116'use server';117118import { updateTag } from 'next/cache';119120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125126 updateTag(`product:${productId}`);127}128```129130Il 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.131132## Turbopack stabile: la parte che senti ogni giorno133134Next.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.135136Detto questo, non migrerei una codebase piena di plugin custom a occhi chiusi. Controllerei:137138- build locale;139- moduli non standard;140- MDX, SVG e CSS;141- plugin Webpack rimasti;142- pagine critiche;143- differenze nei tempi di build.144145Per i progetti nuovi, partirei dal default. Per quelli maturi, farei una migrazione misurata.146147## React Compiler: togliere rumore, non pensiero148149React 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".150151```typescript152// next.config.ts153import type { NextConfig } from 'next';154155const nextConfig: NextConfig = {156 reactCompiler: true,157};158159export default nextConfig;160```161162Non 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.163164Il modo sano di provarlo:1651661. 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ù.171172L'obiettivo non è cancellare ogni `useMemo`. L'obiettivo è smettere di scrivere memoization preventiva perché abbiamo paura del render.173174## `proxy.ts` e il confine di rete175176Il 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.177178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188189 return NextResponse.next();190}191```192193La regola qui è semplice: tienilo piccolo. Redirect, auth routing, header, rewrites essenziali. Se inizia a sembrare un secondo backend, probabilmente sta facendo troppo.194195## Come migrerei davvero196197Non accenderei tutte le feature insieme. Farei così:1981991. 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.208209La migrazione buona non è quella che usa tutte le feature nuove. È quella che rende il comportamento dell'app più leggibile.210211## Cosa cambia nel modo di pensare212213La 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".214215All'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.216217## Fonti utili218219- [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
:Next.js 16, Cache Components e React Compiler: cosa cambia davverolines 1-223 (END) — press q to close