NAME
nextjs-16-cache-components — Next.js 16, Cache Components et React Compiler : ce qui change vraiment
SYNOPSIS
cat nextjs-16-cache-components.md
DESCRIPTION
Pendant des années, l'une des questions les plus ennuyeuses de Next.js a été : « Cette page est-elle statique ou dynamique ? Cela semble être une question simple, jusqu'à ce que vous ajoutiez un appel à cookies(), un fetch avec différentes options, un client de base de données, un CMS, un panier ou un élément de contenu personnalisé.
Next.js 16 est intéressant car il tente de rendre cette conversation moins mystérieuse. Cela n'élimine pas la complexité, mais cela modifie le modèle mental : les routes sont dynamiques par défaut, le cache se déclare là où c'est nécessaire et Suspense devient le moyen naturel de composer des shells rapides avec des parties qui restent fraîches.
La fonctionnalité à comprendre est les composants de cache. Stable Turbopack, React Compiler, proxy.ts et les nouvelles API d'invalidation sont importants, mais ils tournent autour du même problème : créer des applications rapides sans avoir à deviner ce que le framework a décidé en coulisses.
Parce que cette chose compte
Dans une vraie application, vous n'avez pas seulement des « pages statiques » et des « pages dynamiques ». Vous avez différentes pièces avec des besoins différents.
La fiche produit peut changer plusieurs fois par jour. Le prix peut changer plus souvent. La disponibilité doit être quasiment réelle. Le nom d'utilisateur est personnel. Les avis peuvent être diffusés en streaming. La barre latérale peut être stable. Ce n’est pas le cas du chariot.
Si vous traitez tout comme une seule unité, vous vous retrouvez toujours dans l’un des deux extrêmes suivants :
- mise en cache agressive et risque de voir d'anciennes données ;
- rendu dynamique partout et performances moins bonnes que nécessaire.
Cache Components sert précisément à éviter ce faux choix.
Le modèle en pratique
Avec cacheComponents: true, vous pouvez déclarer ce qui peut être mis en cache en utilisant "use cache". Ensuite, vous pouvez associer la durée et les balises à cacheLife() et cacheTag(). Les pièces dynamiques restent dynamiques et peuvent être isolées avec Suspense.
La configuration est petite :
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { cacheComponents: true, }; export default nextConfig;
Le gros changement n'est pas dans la config. C'est dans la façon dont vous commencez à écrire les composants.
// 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> </> ); }
Il n'est pas nécessaire que la page soit entièrement mise en cache ou entièrement dynamique. La fiche produit peut être rapide et réutilisable. L’inventaire peut rester frais. L'utilisateur voit quelque chose immédiatement, sans attendre la partie la plus lente.
use cache est une documentation exécutable
Ce que j'aime dans "use cache", c'est que cela vous oblige à exprimer une intention explicite. Quand on lit une fonction, on comprend tout de suite que quelqu'un a décidé : "ces données peuvent être réutilisées".
C'est particulièrement utile lorsque vous n'utilisez pas fetch. De nombreuses applications lisent les données de Prisma, Drizzle, des SDK internes, des clients CMS ou des fonctions de service. Dans ces cas, l'ancien raisonnement basé uniquement sur les options fetch n'était pas suffisant.
Une règle générale :
- contenu du cache relativement stable ;
- utilisez des balises granulaires ;
- laisse les autorisations dynamiques, les sessions, les paniers, les notifications et les états transactionnels ;
- placez les parties lentes à l'intérieur de
Suspense; - mesurer avant de dire « nous avons amélioré les performances ».
Invalider sans tout jeter
Le cache n'est utile que si vous pouvez le mettre à jour avec précision. Ici, cacheTag, revalidateTag et updateTag deviennent importants.
Exemple :
'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}`); }
Le détail important est l'étiquette. product:${productId} indique une limite précise. products indique un énorme seau. Au début, l’énorme seau est confortable ; après quelques mois, cela devient la raison pour laquelle vous invalidez la moitié d'une application pour changer un titre.
Stable Turbopack : la partie que vous entendez tous les jours
Next.js 16 place Turbopack au centre du développement et de la construction. Ce n'est pas la fonctionnalité la plus poétique, mais c'est celle que l'on ressent pendant que l'on travaille : un serveur qui démarre plus tôt, un rafraîchissement plus rapide, des builds qui ne ressemblent plus à une pause café forcée.
Cela dit, je ne migrerais pas une base de code pleine de plugins personnalisés les yeux fermés. Je vérifierais :
- construction locale ;
- importation non standard ;
- MDX, SVG et CSS ;
- Plugins Webpack restants ;
- pages critiques ;
- différences dans les temps de construction.
Pour les nouveaux projets, je partirais de la valeur par défaut. Pour les plus matures, je ferais une migration mesurée.
React Compiler : supprime le bruit, pas pensé
React Compiler 1.0 est stable et Next.js 16 le prend en charge avec reactCompiler. La promesse est de réduire beaucoup de mémorisation manuelle : moins de memo, moins de useMemo, moins de useCallback utilisé « par sécurité ».
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { reactCompiler: true, }; export default nextConfig;
Je ne le traiterais pas comme une poussière magique. Le compilateur est utile lorsque le code suit bien les règles de React. Si les composants ont des effets secondaires étranges, des mutations cachées ou des hooks mal utilisés, cela doit d’abord être corrigé.
La façon saine de l’essayer :
- mettre à jour
eslint-plugin-react-hooks; - corriger les violations réelles ;
- l'activer sur une zone contrôlée ;
- mesurer le temps de construction et le comportement ;
- supprimez la mémorisation manuelle uniquement lorsqu'elle n'est plus nécessaire.
Le but n’est pas d’effacer tous les useMemo. Le but est d’arrêter d’écrire de la mémorisation préventive car on a peur du rendu.
proxy.ts et la limite du réseau
L'ancien middleware.ts devient proxy.ts. C'est un changement de nom, mais cela a du sens : ce fichier se situe à la limite de la requête, ce n'est pas un middleware générique de style backend traditionnel.
// 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(); }
La règle ici est simple : restez petit. Redirection, routage d'authentification, en-têtes, réécritures essentielles. Si cela commence à ressembler à un deuxième backend, il en fait probablement trop.
Comment je migrerais vraiment
Je n'activerais pas toutes les fonctionnalités en même temps. Je ferais ceci :
- Je mets à jour Next, React et React DOM ;
- Je lance le codemod officiel ;
- Je corrige les modifications avec rupture sur
params,searchParams,cookies(),headers()etdraftMode(); - Je migre
middleware.tsversproxy.ts; - Je vérifie les builds et les pages critiques ;
- J'active les composants de cache sur une section où le cache crée actuellement des frictions ;
- Je définis des conventions pour les balises et l'invalidation ;
- J'essaie React Compiler séparément ;
- comparaison des métriques avant et après.
La bonne migration n’est pas celle qui utilise toutes les nouvelles fonctionnalités. C'est ce qui rend le comportement de l'application plus lisible.
Ce qui change dans la façon de penser
La chose la plus utile à propos de Next.js 16 est qu'il vous oblige à mieux nommer les intentions. Une fonction ne consiste pas simplement à "récupérer le produit de la base de données". C'est "récupérer le produit, je peux le mettre en cache pendant des heures, je l'invalide avec cette balise". Un composant ne consiste pas simplement à "rendre la page". C'est "c'est le shell rapide, cette pièce est personnelle, elle vient en streaming".
Au début, cela semble être plus de travail. Cela devient alors une forme de calme. Les décisions en matière de performance ne sont plus cachées dans une combinaison de valeurs par défaut, d'heuristiques et de mémoire tribale. Ils sont dans le code.
Sources utiles
METADATA
- date: 2026-05-24
- reading: 8 min
- author: Filippo Spinella
- tags: Next.js, React, Frontend, Performance