React Compiler + shadcn/ui : le développement frontend à l'ère de l'optimisation automatique

Ouvrez React DevTools : un composant shadcn Data Table se re-rend à chaque scroll — même quand les props n’ont pas changé. J’avais déjà plus de 20 useMemo écrits à la main, mais certaines situations limites m’échappaient encore. À ce moment-là, je me suis dit : si seulement un outil pouvait gérer ces optimisations automatiquement.
Deux mois plus tard, React Compiler v1.0 est sorti officiellement. Plus besoin de se demander s’il faut ajouter un useMemo à telle fonction, ni de craindre d’oublier un useCallback. Le compilateur s’en charge au moment du build.
Mais qu’est-ce que cela change pour un projet shadcn/ui ? Faut-il garder la mémorisation écrite à la main après activation du Compiler ? Les composants shadcn posent-ils des problèmes de compatibilité ? Cet article partage le retour d’expérience de ces dernières semaines.
TL;DR
1. Qu’est-ce que React Compiler ?
Franchement, React Compiler n’est pas une « révolution » — c’est plutôt un outil d’automatisation qui fait le travail d’optimisation que vous auriez dû écrire à la main.
Avant, à chaque problème de performance en React, il fallait ajouter manuellement useMemo, useCallback, React.memo. En oublier un seul, et toute la page pouvait ramer. Et cette logique de mémorisation pouvait devenir complexe : analyser les dépendances, gérer les cas limites, éviter la sur-optimisation.
L’idée de React Compiler : puisque ces optimisations suivent des règles, laissez le compilateur les analyser au build et insérer la mémorisation appropriée.
Par exemple, pour un Data Table, j’étais obligé d’écrire :
// Version optimisée à la main (fastidieuse)
const columns = useMemo(() => [
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => row.original.name,
},
// ... autres définitions de colonnes
], []); // le tableau de dépendances à maintenir soi-même
const handleRowClick = useCallback((row) => {
console.log('Clicked:', row);
}, []);
Après activation du Compiler, ce code peut disparaître :
// Version optimisée automatiquement par le Compiler (concise)
const columns = [
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => row.original.name,
},
];
const handleRowClick = (row) => {
console.log('Clicked:', row);
};
Le compilateur analyse les dépendances de ces fonctions au build et décide automatiquement s’il faut les mémoriser. Fini le dilemme « faut-il ajouter un useMemo ? ».
La formulation officielle : « build-time performance optimization ». En clair, le compilateur fait le travail de React.memo à votre place.
2. Activer React Compiler : trois méthodes
Si vous utilisez Next.js 16, bonne nouvelle : le Compiler est déjà intégré. Les autres outils de build demandent une configuration supplémentaire.
Méthode 1 : Next.js 16 (le plus simple)
Next.js 16 intègre React Compiler par défaut. Il suffit d’ajouter une ligne dans next.config.js :
// next.config.js
const nextConfig = {
experimental: {
reactCompiler: true, // activer le Compiler
},
};
export default nextConfig;
Tous les composants React du projet seront optimisés automatiquement. Aucun changement de code requis.
Méthode 2 : Vite + React Compiler Plugin
Pour un projet Vite, installez un plugin Babel :
npm install --save-dev babel-plugin-react-compiler
Puis configurez vite.config.ts :
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react({
babel: {
plugins: [
['babel-plugin-react-compiler', {
// optionnel : spécifier le mode de compilation
// 'mode': 'optimize'
}],
],
},
}),
],
});
Vite appliquera alors le Compiler automatiquement au build.
Méthode 3 : configuration Babel autonome (autres outils)
Si vous utilisez webpack, Rollup ou un autre outil de build, ajoutez le plugin directement dans la config Babel :
// .babelrc ou babel.config.json
{
"plugins": [
["babel-plugin-react-compiler"]
]
}
Tout outil de build utilisant Babel peut ainsi supporter le Compiler.
3. shadcn/ui + React Compiler : retour d’expérience
Voici les résultats concrets. J’ai migré un back-office shadcn/ui d’environ 40 composants. L’expérience globale est bonne, avec quelques détails à surveiller.
Scénario 1 : re-rendus du composant Dialog
Le Dialog shadcn est un composant pur typique — props inchangées, rendu inchangé. En optimisation manuelle, j’oubliais souvent d’ajouter useCallback sur onOpenChange.
Avec le Compiler, ce genre de problème disparaît. Il reconnaît onOpenChange comme une fonction stable (sans dépendance externe) et la mémorise automatiquement.
En test, à l’ouverture du Dialog, le composant parent ne se re-rend plus. Avant, chaque ouverture provoquait un re-rendu du parent — parce que onOpenChange était une nouvelle fonction à chaque fois.
Scénario 2 : Form + validation Zod
Le Form shadcn s’appuie sur React Hook Form + Zod. Avant, pour les règles de validation, j’écrivais :
// Version optimisée à la main
const formSchema = useMemo(() => z.object({
username: z.string().min(2, 'Au moins 2 caractères'),
email: z.string().email('Format d\'e-mail incorrect'),
}), []);
const onSubmit = useCallback((values) => {
console.log(values);
}, []);
Maintenant, ces useMemo/useCallback sont supprimés :
// Version optimisée par le Compiler
const formSchema = z.object({
username: z.string().min(2, 'Au moins 2 caractères'),
email: z.string().email('Format d\'e-mail incorrect'),
});
const onSubmit = (values) => {
console.log(values);
};
Le Compiler juge si ces fonctions sont stables. Si le schéma Zod renvoie le même objet à chaque fois (sans variable externe), il le mémorise automatiquement.
Scénario 3 : rendu du Data Table
Le Data Table shadcn repose sur TanStack Table. C’est le scénario le plus propice aux problèmes de performance — définitions de colonnes, tri, filtrage : chaque point pouvait exiger une optimisation manuelle.
Avec le Compiler, les fonctions de colonnes et les gestionnaires d’événements sont optimisés automatiquement. Mesure du nombre de rendus :
- Version optimisée à la main : 15 rendus/s du tableau au scroll (normal)
- Version non optimisée : 45 rendus/s au scroll (performances médiocres)
- Version optimisée par le Compiler : même résultat que l’optimisation manuelle, 15 rendus/s
Résultat équivalent. Et 30 lignes de code d’optimisation manuelle en moins — la lisibilité s’améliore nettement.
Impact sur la taille du bundle
Beaucoup craignent que le Compiler grossisse le bundle. En pratique, l’impact est minime — la logique d’optimisation est insérée à la compilation, sans code runtime supplémentaire.
Comparaison :
- Sans Compiler : bundle 142 Ko
- Avec Compiler : bundle 144 Ko
+2 Ko, principalement la logique de mémorisation insérée. En échange, vous supprimez les useMemo/useCallback manuels (déjà présents dans le bundle) — l’impact global reste faible.
4. Migration : pièges à éviter
Le Compiler n’est pas parfait. Voici les écueils les plus fréquents lors de la migration.
1. Les conventions de nommage comptent
Le Compiler s’appuie sur le nommage pour inférer les dépendances. Si votre code ressemble à ceci :
// ❌ le Compiler peut mal analyser
function MyComponent(props) {
return <div>{props.data.name}</div>;
}
Il peut ne pas identifier correctement la dépendance props.data. Préférez :
// ✅ nommage explicite
function MyComponent({ data }) {
return <div>{data.name}</div>;
}
Le Compiler peut alors juger plus précisément la dépendance data.
Cette convention est aussi mentionnée dans la doc officielle React — déstructurer les props rend le code plus clair et facilite l’analyse du Compiler.
2. Les règles ESLint changent
Si votre projet utilise react-hooks/exhaustive-deps, les rapports de cette règle évoluent après activation du Compiler.
Avant, une dépendance useMemo manquante déclenchait une erreur ESLint. Maintenant, le Compiler gère les dépendances — la règle perd de son importance.
Ajustez la config ESLint : passez exhaustive-deps en « warn » ou désactivez-la. Le Compiler s’en charge ; la règle devient du bruit.
// .eslintrc
{
"rules": {
"react-hooks/exhaustive-deps": "off" // le Compiler gère les dépendances
}
}
3. Compatibilité des bibliothèques tierces
Certaines bibliothèques tierces peuvent être incompatibles, surtout celles avec une logique d’effets de bord complexe en interne.
Si le build échoue ou si des erreurs apparaissent au runtime après activation :
- Lisez le message d’erreur — souvent un problème de nommage ou de logique dans un composant
- Désactivez le Compiler pour ce composant (commentaire
'use no memo') - Investiguez progressivement pour identifier les composants incompatibles
J’ai rencontré une bibliothèque de drag-and-drop incompatible. Solution : désactiver le Compiler sur ce composant :
'use no memo'; // indiquer au Compiler : ne pas optimiser ce composant
function DraggableList() {
// logique de drag-and-drop...
}
Le Compiler ignore alors ce composant et n’applique aucune optimisation automatique.
4. Quand désactiver le Compiler ?
La plupart des composants fonctionnent bien. Quelques cas où la désactivation est recommandée :
- Effets de bord complexes : timers, animations, manipulation DOM directe — le Compiler peut mal analyser
- Bibliothèque tierce incompatible : logique interne complexe, risque de mauvaise interprétation
- Performances qui se dégradent : dans de rares cas extrêmes, une mémorisation excessive peut augmenter la consommation mémoire
Méthode : ajoutez le commentaire 'use no memo' pour que le Compiler ignore le composant.
5. De l’optimisation manuelle à l’automatique : journal de migration
Voici le processus concret. Projet : back-office avec plus de 40 composants shadcn/ui.
Code avant migration
Environ 50 useMemo/useCallback écrits à la main. La partie Data Table était la plus complexe — colonnes, tri, filtrage, tout optimisé manuellement.
Le code était lourd :
// Data Table avant migration (optimisation manuelle)
const columns = useMemo(() => [
{ accessorKey: 'id', header: 'ID' },
{ accessorKey: 'name', header: 'Name' },
// ... autres colonnes
], []);
const sorting = useMemo(() => [{ id: 'name', desc: true }], []);
const handleSortingChange = useCallback((updater) => {
setSorting(updater);
}, []);
Code après migration
Après activation du Compiler, toute l’optimisation manuelle a été supprimée :
// Data Table après migration (optimisation automatique)
const columns = [
{ accessorKey: 'id', header: 'ID' },
{ accessorKey: 'name', header: 'Name' },
];
const sorting = [{ id: 'name', desc: true }];
const handleSortingChange = (updater) => {
setSorting(updater);
};
Code plus concis, bien plus lisible. Avant, il fallait vérifier chaque tableau de dépendances useMemo ; ce n’est plus nécessaire.
Comparaison de performances
Scores Lighthouse :
- Avant migration : Performance 82, LCP 1,8 s
- Après migration : Performance 85, LCP 1,6 s
Légère amélioration. Surtout grâce à la suppression d’optimisations manuelles superflues — le Compiler n’insère la mémorisation que là où c’est vraiment utile.
Temps de rendu mesuré :
- Version optimisée à la main : ~12 ms en moyenne au scroll du Data Table
- Version Compiler : ~10 ms en moyenne
Équivalent, voire légèrement meilleur. La logique d’optimisation du Compiler est solide.
Retours de l’équipe
Après la migration, j’ai recueilli l’avis de quelques collègues :
- « Plus besoin de se demander s’il faut mémoriser, c’est plus simple »
- « Sans tous ces useMemo, le code est beaucoup plus clair »
- « Un composant incompatible, résolu avec ‘use no memo’, ça va »
Retour globalement positif. Moins de charge mentale — fini de se demander à chaque fonction s’il faut la mémoriser.
Résumé
React Compiler est une évolution importante de l’écosystème React. Pour un projet shadcn/ui, les avantages sont clairs :
- Optimisation automatique : plus de useMemo/useCallback manuels, le Compiler s’en charge au build
- Code simplifié : suppression de l’optimisation manuelle, meilleure lisibilité
- Moins de charge mentale : fini le dilemme « faut-il mémoriser ? »
Points d’attention lors de la migration :
- Conventions de nommage : déstructurer les props, éviter
props.data - Configuration ESLint : ajuster la règle exhaustive-deps
- Compatibilité tierce : en cas de problème, utiliser
'use no memo'
Priorisez Next.js 16 pour tester — activation immédiate, configuration minimale. Pour les autres outils de build, suivez l’exemple Vite et migrez progressivement.
Franchement, après le Compiler, écrire du React ne ressemble plus tout à fait à avant — on écrit du JavaScript « normal », sans penser en permanence aux détails de performance. C’est agréable.
FAQ
FAQ
React Compiler augmente-t-il la taille du bundle ?
Les composants shadcn/ui sont-ils compatibles avec React Compiler ?
Faut-il supprimer les useMemo/useCallback écrits à la main après activation du Compiler ?
Dans quels cas faut-il désactiver le Compiler ?
Quelles exigences de nommage impose le Compiler ?
Comment activer le Compiler dans Next.js 16 et les projets Vite ?
9 min de lecture · Publié le: 31 mars 2026 · Mis à jour le: 27 juil. 2026
Tailwind & shadcn/ui en pratique
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Astro + Tailwind : configurer les styles sans conflit avec les composants islands
Conflits de styles Tailwind CSS avec l'architecture islands d'Astro ? Ce guide explique astro-island/astro-slot, l'intégration correcte de Tailwind v4 et quatre scénarios courants de conflits CSS avec leurs solutions.
Partie 12 sur 14
Suivant
shadcn/ui : dépannage des conflits de styles, composants qui ne s'affichent pas et erreurs de types
Panorama systématique des trois grandes catégories de problèmes courants avec shadcn/ui, avec étapes de diagnostic détaillées et solutions pour localiser et corriger rapidement conflits de styles, échecs de rendu et erreurs TypeScript.
Partie 14 sur 14



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire