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

AI Bot
Par AI Bot ·

Chargement du lecteur de synthèse vocale...

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/useCallback superflus

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 :

  1. Configurer React Compiler dans un projet Next.js 15
  2. Configurer le plugin Babel et l'intégration ESLint
  3. Comprendre ce que le compilateur optimise et comment
  4. Déboguer la sortie compilée pour vérifier les optimisations
  5. Mesurer les améliorations de performance réelles
  6. 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-demo

Vérifiez votre version de React :

npm ls react

Vous 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-compiler

Ces deux packages sont tout ce dont vous avez besoin :

  • babel-plugin-react-compiler — le compilateur principal qui transforme votre code à la compilation
  • eslint-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 :

ModeDescription
"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 lint

Toutes 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 :

  1. Re-rendu de ProductList (état modifié)
  2. Recréation du tableau categories (nouvelle référence à chaque rendu)
  3. Re-rendu de CategoryFilter (nouvelle référence de prop categories)
  4. Recréation du callback onSelect (nouvelle référence de fonction)
  5. 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 :

  1. Mémoïse le calcul de categories
  2. Mémoïse le filtrage de filteredProducts
  3. Mémoïse le callback onSelect
  4. Met en cache les éléments JSX inchangés
  5. Ignore le re-rendu de CategoryFilter quand seul searchQuery change

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 Components

Les 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 dev

Vous 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 :

  1. Ouvrez DevTools, allez dans l'onglet Performance
  2. Cliquez sur Record
  3. Cliquez sur "Incrémenter non-lié" 5 fois
  4. Arrêtez l'enregistrement
  5. 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-compiler

Lancez 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 :

  1. Syntaxe non supportée — assurez-vous d'utiliser des patterns React standards
  2. Conflit de bibliothèque tierce — certaines bibliothèques utilisent des patterns non standards ; utilisez "use no memo" sur les composants wrapper
  3. 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 :

  1. Ajoutez "use no memo" pour isoler le problème
  2. Vérifiez si le composant dépend de l'identité référentielle
  3. Assurez-vous qu'il suit les Règles de React
  4. 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 :

  1. Vos composants sont peut-être déjà efficaces
  2. Le goulot d'étranglement pourrait être le réseau ou la récupération de données, pas le rendu
  3. 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.ts active 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


Vous voulez lire plus de tutoriels? Découvrez notre dernier tutoriel sur Inscription à El Fatoora : Guide Pratique Étape par Étape.

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

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.

30 min read·