Next.js 16, Cache Components en React Compiler: wat er echt verandert
· 7 min read · Filippo Spinella · Next.js, React, Frontend, Performance
Een van de meest vervelende vragen in Next.js is al jaren: "Is deze pagina statisch of dynamisch?". Het lijkt een simpele vraag, totdat je een aanroep toevoegt aan cookies(), een fetch met verschillende opties, een databaseclient, een CMS, een winkelwagentje of een stukje aangepaste inhoud.
Next.js 16 is interessant omdat het dit gesprek minder mysterieus probeert te maken. Het elimineert de complexiteit niet, maar het verschuift het mentale model: routes zijn standaard dynamisch, de cache declareert zichzelf waar nodig, en Suspense wordt de natuurlijke manier om snelle shells samen te stellen met onderdelen die actueel blijven.
De functie die u moet begrijpen, is Cache Components. Stabiele Turbopack, React Compiler, proxy.ts en de nieuwe validerings-API's zijn belangrijk, maar ze draaien om hetzelfde probleem: snelle apps bouwen zonder te hoeven raden wat het raamwerk achter de schermen besliste.
Omdat dit ding ertoe doet
In een echte app heb je niet alleen ‘statische pagina’s’ en ‘dynamische pagina’s’. Je hebt verschillende stukken met verschillende behoeften.
Het productblad kan enkele keren per dag veranderen. De prijs kan vaker veranderen. Beschikbaarheid moet bijna live zijn. De gebruikersnaam is persoonlijk. Recensies kunnen worden gestreamd. De zijbalk kan stabiel zijn. De kar niet.
Als je alles als één geheel beschouwt, kom je altijd terecht in een van de twee uitersten:
- agressieve caching en risico op het zien van oude gegevens;
- overal dynamische weergave en prestaties slechter dan nodig.
Cache Components dient juist om deze valse keuze te voorkomen.
Het model in de praktijk
Met cacheComponents: true kun je aangeven wat cachebaar is met "use cache". Vervolgens kunt u de duur en tags koppelen aan cacheLife() en cacheTag(). Dynamische delen blijven dynamisch en kunnen worden geïsoleerd met Suspense.
De opstelling is klein:
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { cacheComponents: true, }; export default nextConfig;
De grote verandering zit niet in de configuratie. Het zit in de manier waarop u begint met het schrijven van de componenten.
// app/products/[slug]/page.tsx import { Suspense } from 'react'; import { cacheLife, cacheTag } from 'next/cache'; async function getProduct(slug: string) { 'use cache'; cacheLife('hours'); cacheTag(`product:${slug}`); return db.product.findUnique({ where: { slug } }); } async function ProductDetails({ slug }: { slug: string }) { const product = await getProduct(slug); return ( <section> <h1>{product.name}</h1> <p>{product.description}</p> </section> ); } async function LiveInventory({ slug }: { slug: string }) { const inventory = await db.inventory.findFirst({ where: { slug } }); return <p>{inventory.quantity} pezzi disponibili</p>; } export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; return ( <> <ProductDetails slug={slug} /> <Suspense fallback={<p>Controllo disponibilità...</p>}> <LiveInventory slug={slug} /> </Suspense> </> ); }
De pagina hoeft niet volledig in de cache of dynamisch te zijn. Het productblad kan snel en herbruikbaar zijn. Voorraad kan vers blijven. De gebruiker ziet meteen iets, zonder te wachten op het langzaamste deel.
use cache is uitvoerbare documentatie
Wat ik leuk vind aan "use cache" is dat het je dwingt een intentie expliciet te maken. Als je een functie leest, begrijp je meteen dat iemand heeft besloten: “deze gegevens kunnen worden hergebruikt”.
Het is vooral handig als u fetch niet gebruikt. Veel apps lezen gegevens van Prisma, Drizzle, interne SDK's, CMS-clients of servicefuncties. In die gevallen was de oude redenering, die alleen op fetch-opties was gebaseerd, niet voldoende.
Een vuistregel:
- cachea-inhoud relatief stabiel;
- gebruik gedetailleerde tags;
- laat dynamische machtigingen, sessies, winkelwagentjes, meldingen en transactiestatussen achter;
- plaats langzame delen in
Suspense; - meet voordat u zegt: "we hebben de prestaties verbeterd".
Maak het ongeldig zonder alles weg te gooien
De cache is alleen nuttig als u deze nauwkeurig kunt bijwerken. Hier worden cacheTag, revalidateTag en updateTag belangrijk.
Voorbeeld:
'use server'; import { updateTag } from 'next/cache'; export async function updateProductName(productId: string, name: string) { await db.product.update({ where: { id: productId }, data: { name }, }); updateTag(`product:${productId}`); }
Het belangrijke detail is het label. product:${productId} geeft een precieze grens aan. products vertelt een enorme emmer. In eerste instantie is de enorme emmer comfortabel; na een paar maanden wordt het de reden dat je een halve app ongeldig maakt om een titel te wijzigen.
Stabiele Turbopack: het deel dat je elke dag hoort
Next.js 16 brengt Turbopack naar het centrum voor ontwikkeling en bouw. Het is niet de meest poëtische functie, maar wel degene die je voelt terwijl je werkt: een server die eerder start, een snellere verversing, builds die niet meer aanvoelen als een gedwongen koffiepauze.
Dat gezegd hebbende, zou ik een codebase vol aangepaste plug-ins niet met mijn ogen dicht migreren. Ik zou controleren:
- lokale bouw;
- niet-standaard import;
- MDX, SVG en CSS;
- Webpack-plug-ins over;
- kritische pagina's;
- verschillen in bouwtijden.
Voor nieuwe projecten zou ik uitgaan van de standaard. Voor volwassen zou ik een afgemeten migratie uitvoeren.
React Compiler: verwijder ruis, niet gedachte
React Compiler 1.0 is stabiel en Next.js 16 ondersteunt het met reactCompiler. De belofte is om veel handmatig onthouden te verminderen: minder memo, minder useMemo, minder useCallback gebruikt "voor de veiligheid".
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { reactCompiler: true, }; export default nextConfig;
Ik zou het niet als magisch stof behandelen. De compiler helpt als de code de React-regels goed volgt. Als componenten vreemde bijwerkingen, verborgen mutaties of slecht gebruikte hooks hebben, moet dat eerst worden opgelost.
De gezonde manier om het te proberen:
- update
eslint-plugin-react-hooks; - feitelijke overtredingen oplossen;
- schakel het in op een gecontroleerd gebied;
- meet bouwtijd en gedrag;
- verwijder handmatige memo's alleen als deze niet langer nodig zijn.
Het doel is niet om elke useMemo te wissen. Het doel is om te stoppen met het schrijven van preventieve memo's, omdat we bang zijn voor weergave.
proxy.ts en de netwerkgrens
De oude middleware.ts wordt proxy.ts. Het is een naamswijziging, maar het is logisch: dat bestand bevindt zich op de verzoekgrens en is geen traditionele generieke middleware in backend-stijl.
// proxy.ts import { NextRequest, NextResponse } from 'next/server'; export default function proxy(request: NextRequest) { const isLoggedIn = Boolean(request.cookies.get('session')); if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); }
De regel hier is simpel: houd het klein. Omleiding, auth-routing, headers, essentiële herschrijvingen. Als het begint te voelen als een tweede backend, doet het waarschijnlijk te veel.
Hoe ik echt zou migreren
Ik zou niet alle functies tegelijk inschakelen. Ik zou dit doen:
- Ik update Next, React en React DOM;
- Ik start de officiële codemod;
- Ik repareer de belangrijkste wijzigingen in
params,searchParams,cookies(),headers()endraftMode(); - Ik migreer
middleware.tsnaarproxy.ts; - Ik controleer builds en kritische pagina's;
- Ik schakel Cache Components in op een sectie waar de cache momenteel voor wrijving zorgt;
- Ik definieer conventies voor tags en ongeldigverklaring;
- Ik probeer React Compiler afzonderlijk;
- vergelijking van statistieken voor en na.
De goede migratie is niet degene die alle nieuwe functies gebruikt. Het maakt het gedrag van de app leesbaarder.
Wat verandert er in de manier van denken
Het handigste aan Next.js 16 is dat het je dwingt om intenties beter te benoemen. Een functie is niet alleen maar "haal het product uit de database". Het is "haal het product, ik kan het urenlang in de cache bewaren, ik maak het ongeldig met deze tag". Een component is niet alleen maar "geef de pagina weer". Het is "dit is de snelle shell, dit stuk is persoonlijk, dit komt streaming."
In eerste instantie lijkt het meer werk. Dan wordt het een vorm van kalmte. Prestatiebeslissingen zijn niet langer verborgen in een combinatie van standaardwaarden, heuristieken en tribaal geheugen. Ze staan in de code.