Primitifs SSR de React 19.2 : Activity, cacheSignal et PPR expliqués
Découvrez comment le nouveau composant Activity, cacheSignal et le pré-rendering partiel de React 19.2 permettent aux développeurs d’avoir un contrôle direct sur les performances du rendu serveur.
Les travaux visant à améliorer les performances de React se répartissent généralement en deux catégories : accélérer et simplifier le rendu initial sur serveur, ainsi que faire en sorte que l’application côté client évite les rendus inutiles une fois montée. Ces deux parties de cet article abordent le problème depuis des angles opposés, mais elles partagent la même philosophie de base — à savoir que la vitesse provient du fait de dire à React précisément quelles tâches sont importantes, lesquelles peuvent être différées et lesquelles ne doivent jamais être exécutées, plutôt que d’ajouter plus de mémoisation ou plus de matériel pour résoudre le problème. La première partie traite du rendu sur serveur et des nouvelles primitives introduites dans React 19.2 ; la seconde porte sur les techniques courantes côté client qui permettent à une application montée de rester réactive.
Réfléchir à nouveau aux performances du rendu sur serveur dans React 19.2
La plupart des conseils concernant les « performances SSR » se résument à accumuler des couches de mise en cache en espérant le meilleur. React 19.2, sorti en octobre 2025, vous offre plutôt des primitives dédiées pour contrôler directement le travail du serveur. Considérer cette version comme une simple correction mineure signifie manquer des gains de vitesse réels — ce sont les détails présentés ci-dessous qui ont un véritable impact.
Garder les composants actifs plutôt que de les détruire
Un problème récurrent dans les applications SSR est le fait que le changement de onglet, l’ouverture d’un modal ou les transitions de route détruisent complètement les composants, effaçant leur état et obligeant à récupérer les données à nouveau. Le nouveau composant Activity a été conçu spécifiquement pour résoudre ce problème.
// old way — full unmount, state gone, effects re-run
{activeTab === 'analytics' && <AnalyticsPanel />}
// React 19.2 — stays alive, just deprioritized
<Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
<AnalyticsPanel />
</Activity>
En maintenant le contenu caché chargé au lieu de le supprimer, React peut précharger ce qui se trouve à l’intérieur d’un bloc Activity caché avant même que l’utilisateur n’y accède. Cette approche permet de réduire significativement la latence perçue lors de la navigation dans des tableaux de bord utilisant beaucoup de SSR, car il n’y a ni rechargement ni changement de mise en page lorsque le contenu devient visible.
Laissez cacheSignal nettoyer automatiquement les tâches abandonnées
Au préalable à la version 19.2, si un rendu serveur était interrompu en cours de route — par exemple parce que l’utilisateur naviguait ailleurs ou qu’une requête expirait — les requêtes en cours et les données mémorisées ne pouvaient pas savoir que cette tâche n’était plus nécessaire. cacheSignal résout ce problème en fournissant aux composants serveurs de React un signal de cycle de vie réel auquel ils peuvent s’attacher pour effectuer le nettoyage.
async function getUserOrders(userId, { signal }) {
const res = await fetch(`/api/orders/${userId}`, { signal });
return res.json();
}
Lorsque la durée de vie du cache est épuisée, le signal associé déclenche une interruption, ce qui empêche les requêtes orphelines de continuer à consommer des ressources CPU sur le serveur en période de pic de trafic.
Créer une coquille statique et diffuser le reste
Le pré-rendering partiel (PPR) est la principale fonctionnalité SSR de cette version. L’idée consiste à créer une fois la coquille statique d’une page — navigation, mise en page, pied de page — à la servir directement depuis un CDN edge, et à diffuser les éléments dynamiques à l’intérieur des limites définies par Suspense.
<Suspense fallback={<ProductSkeleton />}>
<PartialPreRender>
<PersonalizedRecommendations userId={user.id} />
</PartialPreRender>
</Suspense>
Grouper plusieurs affichages de Suspense ensemble
Auparavant, lorsque plusieurs limites de Suspense se résolvaient approximativement au même moment, l’interface utilisateur pouvait présenter un effet « popcorn », avec des fragments de contenu apparaissant les uns après les autres plutôt que simultanément. React 19.2 regroupe ces affichages afin que le comportement côté client et côté serveur reste cohérent, et il ajoute un support pour les Web Streams dans Node.js destiné aux équipes ayant besoin d’un contrôle plus fin sur le streaming.
Ces API élèvent les limites, pas les seuils inférieurs
Rien de tout cela ne remplace les principes fondamentaux. Vous devez encore éliminer les schémas de récupération de données N+1 et décomposer les bundles monolithiques avant que ces fonctionnalités ne puissent vous être utiles. React 19.2 n’abaisse pas le volume minimal d’optimisation requis — il élève plutôt la limite maximale de vitesse atteignable par une application bien optimisée. Il est également utile de lire les notes officielles de version de React 19.2, ainsi que les paramètres par défaut de Turbopack introduits dans Next.js 16, qui s’accordent bien avec PPR.
Déjà en 2026, les performances SSR ne dépendent plus d’un cacheage plus agressif — elles dépendent plutôt de la capacité à indiquer à React ce qui peut attendre, ce qui peut être diffusé en flux continu, et ce qui peut échouer de manière propre. React 19.2 est enfin ce qui vous fournit le vocabulaire nécessaire pour exprimer cela.
En dehors de ces mécanismes spécifiques aux SSR, beaucoup de ce qui fait que une application React semble rapide repose sur des habitudes quotidiennes qui restent valables quel que soit le stratégie de rendu ou la version de React. Alors que la section précédente se concentrait sur le streaming, le pré-rendu et Suspense du côté serveur, ce qui suit aborde les patterns côté client qui permettent à toute application React de rester réactive dans la pratique.
Techniques pratiques pour une performance React quotidienne
React se rend déjà de manière efficace par défaut. C’est à mesure que l’application grandit que des re-rendus inutiles, des listes trop volumineuses, un excès de JavaScript et trop d’appels réseau commencent à s’accumuler. La solution ne nécessite rarement des astuces exotiques — quelques habitudes disciplinées suffisent généralement pour y parvenir.
Rendre uniquement ce qui doit vraiment être mis à jour
Chaque réaffichage pousse React à exécuter à nouveau le corps de la fonction d’un composant. Ce n’est pas en soi un problème — le véritable coût réside dans la répétition de tâches coûteuses lorsque rien d’important n’a vraiment changé. Évitez de placer des états non liés à l’intérieur d’un composant qui affiche une grande partie de votre interface, car mettre à jour cet état force alors tout le sous-arbre à se réafficher en même temps. Préférez plutôt diviser les composants afin qu’une mise à jour n’affecte que la partie de l’interface censée être modifiée. L’objectif n’est pas zéro réaffichage — c’est d’éliminer ceux qui sont inutiles.
Placer les états là où ils sont réellement nécessaires
Résistez à la tentation de monter chaque état en haut de l’arbre des composants. Si un seul composant lit une valeur donnée, c’est précisément là qu’elle doit se trouver.
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}
Conserver l’état de manière locale limite la portée des mises à jour, ce qui réduit les redessins accidentels ailleurs dans l’arborescence. En règle générale, placez l’état près du composant qui en a besoin plutôt qu’en haut de la hiérarchie.
Dériver les valeurs plutôt que de les stocker
Tout ne doit pas être placé dans useState. Si vous suivez déjà firstName et lastName, il n’y a aucune raison de stocker également fullName séparément. La version gaspilleuse ressemble à ceci :
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
Une approche plus simple consiste simplement à calculer la valeur pendant le rendu :
const fullName = `${firstName} ${lastName}`;
Cela élimine à la fois une variable d’état supplémentaire et un Effect inutile. En général, si une valeur peut être calculée en temps réel pendant le rendu, elle n’a probablement pas besoin d’être stockée en tant qu’état.
Gérer de grandes listes sans surcharger le DOM
Le rendu de milliers de nœuds en même temps devient rapidement coûteux. Imaginez une interface de chat contenant 10 000 messages — il n’est pas nécessaire qu’ils soient tous présents dans le DOM en même temps. Pour de grandes collections, optez pour l’une des solutions suivantes :
- Virtualisation
- Pagination
- Défilement infini
La virtualisation ne garde que les lignes actuellement visibles, ainsi qu’un petit buffer, à tout moment donné ; des bibliothèques comme react-window mettent en œuvre ce principe à votre place. Cela dit, n’appliquez pas la virtualisation de manière automatique — une liste de 50 éléments n’en a presque certainement pas besoin.
Différez le chargement du code jusqu’à ce qu’il soit nécessaire
Les utilisateurs ne devraient pas avoir à télécharger du JavaScript pour des fonctionnalités qu’ils n’ont pas encore ouvertes. Les API lazy et Suspense de React vous permettent de séparer ce code du bundle initial :
const Settings = lazy(() => import("./Settings"));
Avec cette solution, le composant Settings n’est chargé qu’une fois qu’il est réellement affiché, et non lors du chargement initial de la page. Cela est avantageux pour des fonctionnalités lourdes ou peu utilisées — graphiques, éditeurs, cartes, écrans de paramètres, grands tableaux de bord — et permet généralement un chargement initial plus rapide.
Conserver une interface interactive réactive sous charge
Toutes les mises à jour n’ont pas besoin d’avoir lieu en parfaite synchronisation avec les actions de l’utilisateur. Une boîte de recherche doit réagir instantanément aux frappes, même si le filtrage d’un grand ensemble de données s’exécute en arrière-plan avec une priorité moindre. Les hooks useTransition et useDeferredValue de React sont conçus précisément pour ce type d’équilibre. Le débouncing des entrées brutes produit un effet similaire :
const debouncedSearch = useDebounce(search, 500);
Au lieu d’envoyer une requête à chaque frappe, attendez que l’utilisateur fasse une pause. Ces approches sont utiles pour les champs de recherche, les filtres, les listes longues et les tableaux de bord complexes.
Réduire les requêtes réseau redondantes
La vitesse de rendu n’est qu’un aspect — un trop grand nombre de requêtes en cours peut rendre une application lente, même si son rendu est rapide. Selon la situation, envisagez :
- Le stockage en cache des réponses
- La suppression des requêtes redondantes
- La pagination des résultats
- L’atténuation des frappes dans les champs de recherche
- La suppression des requêtes qui ne sont plus pertinentes
Si un utilisateur tape rapidement
react
react performance
react performance optimization
vous ne voulez probablement pas que trois requêtes distinctes fonctionnent en même temps. Le principe de base reste simple : évitez de faire effectuer au réseau des tâches dont vous n’avez pas réellement besoin.
Utiliser la mémorisation de manière sélective
React fournit trois primitives de mémorisation courantes :
- useMemo cache le résultat d’un calcul.
- useCallback cache une référence à une fonction entre plusieurs rendus.
- React.memo permet d’éviter de rérender un composant lorsque ses props n’ont pas changé.
Un exemple typique :
const filteredUsers = useMemo(() => {
return users.filter(user =>
user.name.includes(search)
);
}, [users, search]);
Cependant, la mémorisation n’est pas gratuite — elle entraîne des coûts supplémentaires et ajoute de la complexité au code environnant. L’utilisez lorsque un calcul est réellement coûteux, lorsqu’un composant se rérender sans raison, ou lorsque une référence stable est vraiment importante plus bas dans l’arborescence. Ne perdez pas de temps à optimiser du code qui ne pose pas de problème mesurable.
Laissez le compilateur assumer une partie du travail
Un ajout récent à l’outilchain React, le React Compiler, peut appliquer automatiquement de nombreuses de ces optimisations à votre place — mémorisant des valeurs, des fonctions et des composants sans que vous ayez à le faire manuellement dans chaque cas. Cela signifie que vous n’avez plus besoin de recourir systématiquement à :
useMemo(...)
useCallback(...)
React.memo(...)
Néanmoins, le React Compiler ne remplace pas la nécessité de vérifier d’abord s’il existe réellement un problème de performance. La bonne procédure reste de confirmer qu’il y a bien un problème réel, de laisser le compilateur appliquer les optimisations dont il est capable, et de recourir à la mémorisation manuelle uniquement lorsqu’on a une raison concrète.
Fournir des identités stables à React
Les clés indiquent à React quel élément d’une liste correspond à tel ou tel autre lors des différentes rendus. Préférez dériver la clé d’une propriété stable et unique des données plutôt que de sa position :
items.map(item => (
<Item key={item.id} />
));
Évitez d’utiliser l’index de tableau comme clé, car le réordonnancement, l’insertion ou la suppression d’éléments déplace tous les indices en dessous du point de modification :
items.map((item, index) => (
<Item key={index} />
));
Une clé stable permet à React de détecter correctement quels éléments ont été ajoutés, supprimés ou mis à jour, au lieu de deviner en se basant sur leur position. Le même principe s’applique aux objets et fonctions transmis en tant que props : créer un objet ou une fonction de rappel entièrement nouvelle à chaque rendu annule l’intérêt d’un composant enfant mémorisé, car ses props sembleront différents à chaque fois même si rien de significatif n’a changé.
Localisez le véritable goulot d’étranglement avant d’agir
Lorsque vous maîtriserez ces techniques, ne vous fiez pas à votre intuition pour décider de ce qu’il faut corriger. Ouvrez le Profilleur des outils de développement React pour voir précisément quels composants s’affichent et combien de temps chaque élément met à se charger. Pour les problèmes qui dépassent React lui-même, le panneau Performance du navigateur peut révéler des tâches longues, une exécution lente des scripts, des opérations de mise en page coûteuses ou des goulots d’étranglement dans le rendu. Plutôt que de supposer « ce composant semble lent », utilisez ces outils pour déterminer précisément pourquoi il est lent.
Vérifier que la correction a vraiment fonctionné
Après avoir appliqué une optimisation, mesurez à nouveau plutôt que de supposer qu’elle a fonctionné. Vérifiez si le temps de rendu a réellement diminué, si le fichier compressé est devenu plus petit, et si les interactions semblent plus rapides. Si rien de tout cela n’a été amélioré, la modification n’était peut-être pas nécessaire dès le départ.
Une liste de contrôle à effectuer avant la mise en ligne
Lectures complémentaires
- Comprendre React Lanes : comment les masques-bits codent la priorité des mises à jour — Découvrez comment React encode plusieurs priorités de mise à jour en un seul entier à l’aide de masques-bits, et pourquoi les opérations bit par bit remplacent les simples flags booléens pour le planification.
- Le compilateur Go de TypeScript et l’exécution native : un guide de migration — Apprenez comment le compilateur basé sur Go de TypeScript et l’exécution native de Node.js affecteront les bases de code React et Next.js, ainsi que ce qu’il convient de corriger dans votre tsconfig dès maintenant.