React Compiler avec Next.js : guide complet d'optimisation automatique

Prérequis
Avant de commencer ce tutoriel, assurez-vous de disposer de :
- Node.js 20+ installé
- Un projet Next.js 15+ (App Router recommandé)
- Une compréhension basique des hooks React et de la mémoïsation (
useMemo,useCallback,React.memo) - Un éditeur de code comme VS Code
Qu'est-ce que React Compiler ?
React Compiler (anciennement connu sous le nom de React Forget) est un outil de compilation qui optimise automatiquement vos composants React en insérant la mémoïsation là où elle est nécessaire. Au lieu d'envelopper manuellement les valeurs dans useMemo, les callbacks dans useCallback et les composants dans React.memo, le compilateur analyse votre code et le fait pour vous.
Cela signifie :
- Plus de mémoïsation manuelle — le compilateur s'en charge
- Tailles de bundles réduites — les re-rendus inutiles sont éliminés à la compilation
- Meilleures performances par défaut — chaque composant est optimisé sans effort du développeur
- Code plus propre — suppression des wrappers
useMemo/useCallbacksuperflus
React Compiler comprend les Règles de React et transforme votre code en toute sécurité tout en préservant la correction.
Ce que vous allez construire
Dans ce tutoriel, vous allez :
- Configurer React Compiler dans un projet Next.js 15
- Configurer le plugin Babel et l'intégration ESLint
- Comprendre ce que le compilateur optimise et comment
- Déboguer la sortie compilée pour vérifier les optimisations
- Mesurer les améliorations de performance réelles
- Gérer les cas limites et les scénarios de désactivation
Étape 1 : Créer un projet Next.js
Si vous avez déjà un projet Next.js 15+, passez à l'étape 2. Sinon, créez-en un nouveau :
npx create-next-app@latest react-compiler-demo --typescript --tailwind --app --src-dir
cd react-compiler-demoVérifiez votre version de React :
npm ls reactVous devriez voir react@19.x.x. React Compiler nécessite React 19 ou ultérieur.
Étape 2 : Installer React Compiler
Installez le plugin Babel du compilateur et le plugin ESLint :
npm install -D babel-plugin-react-compiler eslint-plugin-react-compilerCes deux packages sont tout ce dont vous avez besoin :
babel-plugin-react-compiler— le compilateur principal qui transforme votre code à la compilationeslint-plugin-react-compiler— signale les violations des Règles de React qui empêcheraient une compilation sûre
Étape 3 : Configurer Next.js
Next.js 15 intègre nativement le support de React Compiler. Activez-le dans votre next.config.ts :
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;Cette seule ligne suffit pour activer le compilateur pour tout votre projet. Next.js gère automatiquement l'intégration du plugin Babel.
Configuration avancée
Pour plus de contrôle, vous pouvez passer des options :
const nextConfig: NextConfig = {
experimental: {
reactCompiler: {
compilationMode: "annotation", // Compiler uniquement les composants annotés
panicThreshold: "CRITICAL_ERRORS", // Échec de build sur erreurs critiques
},
},
};Les options de compilationMode :
| Mode | Description |
|---|---|
"infer" | Par défaut. Compile tous les composants et hooks automatiquement |
"annotation" | Compile uniquement les composants/hooks avec la directive "use memo" |
"all" | Compile tout, même si cela viole les Règles de React (à utiliser avec prudence) |
Pour la plupart des projets, le mode par défaut "infer" est le bon choix.
Étape 4 : Configurer l'intégration ESLint
Ajoutez le plugin ESLint pour détecter les violations des Règles de React en amont. Mettez à jour votre .eslintrc.json :
{
"plugins": ["react-compiler"],
"rules": {
"react-compiler/react-compiler": "error"
}
}Ou si vous utilisez la configuration ESLint plate (eslint.config.mjs) :
import reactCompiler from "eslint-plugin-react-compiler";
export default [
{
plugins: {
"react-compiler": reactCompiler,
},
rules: {
"react-compiler/react-compiler": "error",
},
},
];Lancez ESLint pour vérifier votre base de code :
npx next lintToutes les violations apparaîtront comme des erreurs, vous indiquant exactement ce qu'il faut corriger avant que le compilateur puisse optimiser votre code en toute sécurité.
Étape 5 : Comprendre ce qui est optimisé
Examinons un exemple pratique. Considérez ce composant avant React Compiler :
// components/ProductList.tsx — avant optimisation
"use client";
import { useState } from "react";
interface Product {
id: number;
name: string;
price: number;
category: string;
}
function ProductCard({ product }: { product: Product }) {
console.log(`Rendering ProductCard: ${product.name}`);
return (
<div className="border rounded-lg p-4">
<h3 className="font-bold">{product.name}</h3>
<p className="text-gray-600">${product.price}</p>
</div>
);
}
function CategoryFilter({
categories,
selected,
onSelect,
}: {
categories: string[];
selected: string;
onSelect: (cat: string) => void;
}) {
console.log("Rendering CategoryFilter");
return (
<div className="flex gap-2 mb-4">
{categories.map((cat) => (
<button
key={cat}
onClick={() => onSelect(cat)}
className={selected === cat ? "bg-blue-500 text-white px-3 py-1 rounded" : "bg-gray-200 px-3 py-1 rounded"}
>
{cat}
</button>
))}
</div>
);
}
export default function ProductList({ products }: { products: Product[] }) {
const [selectedCategory, setSelectedCategory] = useState("All");
const [searchQuery, setSearchQuery] = useState("");
const categories = ["All", ...new Set(products.map((p) => p.category))];
const filteredProducts = products.filter((p) => {
const matchesCategory = selectedCategory === "All" || p.category === selectedCategory;
const matchesSearch = p.name.toLowerCase().includes(searchQuery.toLowerCase());
return matchesCategory && matchesSearch;
});
return (
<div>
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search products..."
className="border rounded px-3 py-2 mb-4 w-full"
/>
<CategoryFilter
categories={categories}
selected={selectedCategory}
onSelect={setSelectedCategory}
/>
<div className="grid grid-cols-3 gap-4">
{filteredProducts.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
</div>
);
}Le problème sans React Compiler
Sans le compilateur, chaque frappe dans le champ de recherche provoque :
- Re-rendu de
ProductList(état modifié) - Recréation du tableau
categories(nouvelle référence à chaque rendu) - Re-rendu de
CategoryFilter(nouvelle référence de propcategories) - Recréation du callback
onSelect(nouvelle référence de fonction) - Re-rendu de tous les composants
ProductCard
L'ancien correctif manuel
Avant React Compiler, vous auriez manuellement ajouté :
// Mémoïsation manuelle de tout — fastidieux et sujet aux erreurs
const categories = useMemo(
() => ["All", ...new Set(products.map((p) => p.category))],
[products]
);
const filteredProducts = useMemo(
() => products.filter((p) => { /* ... */ }),
[products, selectedCategory, searchQuery]
);
const handleSelect = useCallback(
(cat: string) => setSelectedCategory(cat),
[]
);
// Plus envelopper les composants enfants dans React.memo()
const ProductCard = React.memo(function ProductCard({ product }: { product: Product }) {
// ...
});Ce que React Compiler fait automatiquement
Avec React Compiler activé, le code original propre (sans useMemo/useCallback) est automatiquement transformé à la compilation. Le compilateur :
- Mémoïse le calcul de
categories - Mémoïse le filtrage de
filteredProducts - Mémoïse le callback
onSelect - Met en cache les éléments JSX inchangés
- Ignore le re-rendu de
CategoryFilterquand seulsearchQuerychange
Vous écrivez du code propre. Le compilateur gère la performance.
Étape 6 : Vérifier la sortie du compilateur
Pour confirmer que le compilateur fonctionne, installez React DevTools et cherchez le badge "Memo" :
# Dans votre navigateur, installez l'extension React DevTools
# Puis ouvrez DevTools → onglet ComponentsLes composants optimisés par React Compiler affichent un badge "Memo ✨" dans React DevTools. Cela confirme que le compilateur a automatiquement mémoïsé ce composant.
Vous pouvez aussi vérifier la sortie compilée directement. Ajoutez ceci à votre next.config.ts pour le débogage :
const nextConfig: NextConfig = {
experimental: {
reactCompiler: {
// Logger les résultats de compilation
logger: {
logEvent(filename, event) {
console.log(`[React Compiler] ${filename}:`, event);
},
},
},
},
};Lancez votre serveur de développement et vérifiez la sortie du terminal :
npm run devVous verrez des logs montrant quels composants ont été compilés et quelles optimisations ont été appliquées.
Étape 7 : Mesurer la performance
Mesurons l'impact réel. Créez un composant de test de performance :
// app/perf-test/page.tsx
"use client";
import { useState, useEffect, useRef } from "react";
function ExpensiveChild({ value }: { value: number }) {
// Simuler un rendu coûteux
const start = performance.now();
while (performance.now() - start < 2) {
// Attente active pendant 2ms
}
return <div className="p-2 border rounded">{value}</div>;
}
export default function PerfTest() {
const [count, setCount] = useState(0);
const [unrelated, setUnrelated] = useState(0);
const renderCount = useRef(0);
const startTime = useRef(0);
useEffect(() => {
renderCount.current++;
});
const items = Array.from({ length: 100 }, (_, i) => i);
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">Test de performance</h1>
<div className="flex gap-4 mb-4">
<button
onClick={() => {
startTime.current = performance.now();
setCount((c) => c + 1);
}}
className="bg-blue-500 text-white px-4 py-2 rounded"
>
Incrémenter lié : {count}
</button>
<button
onClick={() => {
startTime.current = performance.now();
setUnrelated((u) => u + 1);
}}
className="bg-gray-500 text-white px-4 py-2 rounded"
>
Incrémenter non-lié : {unrelated}
</button>
</div>
<p>Nombre de rendus : {renderCount.current}</p>
<div className="grid grid-cols-10 gap-2">
{items.map((i) => (
<ExpensiveChild key={i} value={i + count} />
))}
</div>
</div>
);
}Sans React Compiler : Cliquer sur "Incrémenter non-lié" re-rend les 100 composants ExpensiveChild (environ 200ms de délai).
Avec React Compiler : Cliquer sur "Incrémenter non-lié" ignore tous les re-rendus de ExpensiveChild car leurs props n'ont pas changé (réponse quasi-instantanée).
Utilisez l'onglet Performance de Chrome DevTools pour enregistrer et comparer :
- Ouvrez DevTools, allez dans l'onglet Performance
- Cliquez sur Record
- Cliquez sur "Incrémenter non-lié" 5 fois
- Arrêtez l'enregistrement
- Comparez les flamegraphs avec et sans le compilateur
Étape 8 : Gérer les cas limites
Désactiver la compilation
Si un composant ne fonctionne pas correctement après compilation, vous pouvez le désactiver :
// Ajoutez la directive "use no memo" pour ignorer la compilation
"use no memo";
export default function LegacyComponent() {
// Ce composant ne sera pas compilé
// Utile pour les composants qui dépendent de l'identité référentielle
}Activer (mode annotation)
Si vous utilisez compilationMode: "annotation", activez des composants spécifiques :
// Ajoutez la directive "use memo" pour activer la compilation
"use memo";
export default function OptimizedComponent() {
// Seul ce composant est compilé
}Violations courantes
Le compilateur ignorera les composants qui violent les Règles de React. Problèmes courants :
1. Mutation directe des props ou de l'état :
// MAUVAIS — le compilateur ne peut pas optimiser ceci
function BadComponent({ items }: { items: string[] }) {
items.sort(); // Mutation des props !
return <ul>{items.map((item) => <li key={item}>{item}</li>)}</ul>;
}
// BON — créer un nouveau tableau
function GoodComponent({ items }: { items: string[] }) {
const sorted = [...items].sort();
return <ul>{sorted.map((item) => <li key={item}>{item}</li>)}</ul>;
}2. Appel conditionnel des hooks :
// MAUVAIS — les hooks doivent être appelés inconditionnellement
function BadComponent({ show }: { show: boolean }) {
if (show) {
const [value, setValue] = useState(0); // Hook conditionnel !
}
}
// BON — toujours appeler les hooks au niveau supérieur
function GoodComponent({ show }: { show: boolean }) {
const [value, setValue] = useState(0);
if (!show) return null;
return <div>{value}</div>;
}3. Utilisation de refs pendant le rendu :
// MAUVAIS — lire les refs pendant le rendu casse la mémoïsation
function BadComponent() {
const ref = useRef(0);
ref.current++; // Effet de bord pendant le rendu !
return <div>{ref.current}</div>;
}
// BON — utiliser les refs dans les effets ou gestionnaires d'événements
function GoodComponent() {
const ref = useRef(0);
useEffect(() => {
ref.current++;
});
return <div>Vérifiez la console</div>;
}Étape 9 : Migrer un projet existant
Pour les projets existants, suivez cette stratégie de migration progressive :
Phase 1 : Ajouter le plugin ESLint d'abord
npm install -D eslint-plugin-react-compilerLancez le linter et corrigez toutes les violations avant d'activer le compilateur.
Phase 2 : Activer en mode annotation
// next.config.ts
const nextConfig: NextConfig = {
experimental: {
reactCompiler: {
compilationMode: "annotation",
},
},
};Ajoutez "use memo" à quelques composants et testez minutieusement.
Phase 3 : Passer en mode infer
Une fois confiant, passez au mode par défaut :
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};Phase 4 : Supprimer la mémoïsation manuelle
Vous pouvez maintenant supprimer en toute sécurité les appels manuels à useMemo, useCallback et React.memo :
# Trouver toutes les mémoïsations manuelles dans votre base de code
grep -rn "useMemo\|useCallback\|React.memo" --include="*.tsx" --include="*.ts" src/Supprimez-les une par une, en vérifiant que le compilateur gère correctement chaque cas.
Étape 10 : Bonnes pratiques
À faire
- Écrivez du React propre et idiomatique — le compilateur récompense le code simple
- Suivez les Règles de React — composants purs, pas d'effets de bord pendant le rendu
- Utilisez le plugin ESLint — détectez les violations avant la production
- Testez la performance avec DevTools — vérifiez que le compilateur aide
À éviter
- Ne gardez pas la mémoïsation manuelle avec le compilateur — c'est redondant
- N'utilisez pas
"use no memo"en premier recours — corrigez le problème sous-jacent - Ne vous fiez pas à l'identité référentielle pour la logique métier — le compilateur peut changer quand les objets sont créés
- Ne mutez pas les objets ou tableaux en place — créez toujours de nouvelles références
Composants serveur
React Compiler bénéficie principalement aux composants client ("use client"). Les composants serveur sont rendus une seule fois côté serveur, donc la mémoïsation a moins d'impact. Concentrez vos efforts d'optimisation sur le code interactif côté client.
Dépannage
Erreurs de build après activation du compilateur
Si votre build échoue, vérifiez le message d'erreur. Causes courantes :
- Syntaxe non supportée — assurez-vous d'utiliser des patterns React standards
- Conflit de bibliothèque tierce — certaines bibliothèques utilisent des patterns non standards ; utilisez
"use no memo"sur les composants wrapper - Version de React obsolète — mettez à jour vers React 19+
Le composant se comporte différemment
Si un composant fonctionne différemment après compilation :
- Ajoutez
"use no memo"pour isoler le problème - Vérifiez si le composant dépend de l'identité référentielle
- Assurez-vous qu'il suit les Règles de React
- Signalez le problème sur le dépôt GitHub de React Compiler
Aucune amélioration de performance
Si vous ne constatez pas d'améliorations :
- Vos composants sont peut-être déjà efficaces
- Le goulot d'étranglement pourrait être le réseau ou la récupération de données, pas le rendu
- Utilisez React DevTools Profiler pour identifier le véritable goulot d'étranglement
Conclusion
React Compiler transforme la façon dont nous écrivons des applications React. Au lieu de parsemer le code de useMemo, useCallback et React.memo, vous écrivez des composants propres et simples et laissez le compilateur gérer l'optimisation automatiquement.
Points clés à retenir :
- Une seule ligne dans
next.config.tsactive l'optimisation automatique - Le plugin ESLint détecte les problèmes avant la production
- Le code propre gagne — le compilateur récompense le React idiomatique
- L'adoption progressive est supportée via le mode annotation
- Les gains de performance sont réels — surtout pour les interfaces complexes avec des re-rendus fréquents
L'avenir de la performance React est automatique. Commencez à utiliser React Compiler dès aujourd'hui et laissez votre code être à la fois propre et rapide.
Prochaines étapes
- Lisez la documentation officielle de React Compiler pour les dernières mises à jour
- Explorez le React DevTools Profiler pour l'analyse de performance
- Consultez notre tutoriel sur les React 19 Server Actions pour plus de fonctionnalités React 19
- Découvrez le Next.js 15 Partial Prerendering pour l'optimisation côté serveur
Discutez de votre projet avec nous
Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.
Trouvons les meilleures solutions pour vos besoins.
Articles connexes

Next.js 15 Partial Prerendering (PPR) : Construire un Dashboard Ultra-Rapide avec le Rendu Hybride
Maîtrisez le Partial Prerendering (PPR) de Next.js 15 — combinez rendu statique et dynamique dans une seule page. Construisez un dashboard analytique avec un shell statique instantané et du contenu dynamique en streaming.

Construire une application full-stack en temps réel avec Convex et Next.js 15
Apprenez à construire une application full-stack en temps réel avec Convex et Next.js 15. Ce tutoriel couvre la conception de schémas, les requêtes, les mutations, les abonnements en temps réel, l'authentification et le téléchargement de fichiers — le tout avec une sécurité de types de bout en bout.

Construire une application complète avec Firebase et Next.js 15 : Auth, Firestore et temps réel
Apprenez à créer une application full-stack avec Next.js 15 et Firebase. Ce guide couvre l'authentification, Firestore, les mises à jour en temps réel, les Server Actions et le déploiement sur Vercel.