Mis à jour ces 90 derniers jours 30 nouvelles · 48 révisées
Directive Next.js (expérimentale en 15, activée par cacheComponents: true depuis Next.js 16, 21 octobre 2025) qui rend cachable une page, un composant ou une fonction async. La clé de cache est dérivée automatiquement par le compilateur (ID de build, ID de fonction, arguments et variables capturées). Tout ce qui n'est pas marqué est dynamique par défaut : le cache implicite de l'ancien App Router disparaît. Les portées 'use cache' constituent la coquille statique du Partial Prerendering.
Un plat cuisiné sous vide : préparé une fois, réchauffé à chaque service tant que la date (cacheLife) n'est pas dépassée.
// next.config.ts
export default { cacheComponents: true };
// lib/data.ts
import { cacheLife, cacheTag } from 'next/cache';
export async function getProducts(category: string) {
'use cache';
cacheLife('hours'); // durée explicite recommandée
cacheTag('products'); // invalidation ciblée
return db.products.findMany({ where: { category } });
}
// Composant entier cachable (doit être async)
export async function Footer() { 'use cache'; return <footer>…</footer>; }Cas d'usage : Mettre en cache des requêtes BDD ou des blocs de page coûteux tout en gardant le reste dynamique, avec navigation instantanée côté client.
Directive qui marque un composant comme Client Component, activant l'interactivité (state, effets, handlers). Frontière entre serveur et client dans l'arbre.
Comme le panneau 'zone libre-service' dans un magasin : a partir de la, c'est le client qui interagit.
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}Cas d'usage : Composants interactifs : formulaires, boutons, modales, tout ce qui a besoin de state ou d'event handlers.
Directive qui marque une fonction (ou tout un module) comme Server Function : elle s'exécute sur le serveur mais le client en reçoit une référence appelable, comme un RPC. Elle ne marque PAS un Server Component : les composants serveur sont le défaut, sans directive.
Comme un bouton dans un ascenseur : tu appuies cote client, mais le moteur (serveur) fait tout le travail.
'use server';
export async function createUser(formData) {
const name = formData.get('name');
await db.insert({ name });
revalidatePath('/users');
}Cas d'usage : Mutations de données (formulaires, CRUD) sans créer de route API séparée.
Composant livré dans React 19.2 (1er octobre 2025) qui masque ou révèle un sous-arbre sans le démonter. En mode hidden, le contenu est caché en display: none, l'état React et le DOM (brouillon de textarea, position vidéo) sont conservés, les effets sont nettoyés et le rendu continue à basse priorité. Il permet aussi de pré-rendre en arrière-plan un écran pas encore visible.
Mettre une pièce en veille : lumières éteintes, mais les meubles restent en place pour le retour.
import { Activity } from 'react';
function App({ tab }) {
return (
<>
<Activity mode={tab === 'home' ? 'visible' : 'hidden'}>
<Home /> {/* état conservé quand on change d'onglet */}
</Activity>
<Activity mode={tab === 'posts' ? 'visible' : 'hidden'}>
<Posts /> {/* pré-rendu à basse priorité si caché */}
</Activity>
</>
);
}Cas d'usage : Onglets, barres latérâles, écrans d'un flux multi-étapes dont on veut garder l'état ; pré-chargement du code et des données d'une page probable.
Composant stable depuis React 19.3 (9 septembre 2026), après une phase expérimentale annoncée le 23 avril 2025. Il anime les éléments qui entrent, sortent, se déplacent ou se partagent entre écrans en s'appuyant sur l'API View Transition du navigateur. Il ne se déclenche que dans une transition (startTransition, Suspense, useDeferredValue). addTransitionType permet de choisir l'animation selon la cause (navigation avant/arrière).
Le fondu enchaîné d'un monteur vidéo, appliqué automatiquement entre deux plans de votre interface.
import { ViewTransition, addTransitionType, startTransition } from 'react';
<ViewTransition
enter={{ 'nav-forward': 'slide-left', 'nav-back': 'slide-right', default: 'auto' }}
exit="auto"
>
<Page />
</ViewTransition>
// Dans le routeur
startTransition(() => {
addTransitionType('nav-' + direction);
navigate(url);
});
/* CSS : ::view-transition-old(.slide-left) { animation: ... } */Cas d'usage : Transitions de pages, agrandissement d'une vignette en plein écran (prop name partagée), apparition/disparition d'items de liste.
Fonction associée à une route qui traite les mutations (POST, PUT, DELETE) soumises par un <Form>. Après l'action, React Router revalide automatiquement les loaders de la page. Depuis la v8 (17 juin 2026), l'action peut être précédée d'un middleware (auth, contexte), activé par défaut.
Comme la boite aux lettres d'un bureau : tu deposes ton courrier (formulaire), le bureau (action) le traite.
import { redirect } from 'react-router';
import type { Route } from './+types/new-user';
export async function action({ request }: Route.ActionArgs) {
const form = await request.formData();
await api.createUser(Object.fromEntries(form));
return redirect('/users'); // les loaders sont revalidés
}
// <Form method="post"> déclenche l'action sans JS customCas d'usage : Gérer les soumissions de formulaires avec revalidation automatique des loaders après mutation.
Depuis React 19 (5 décembre 2024), une fonction asynchrone passée à startTransition ou à la prop action d'un <form> est une « Action ». React gère pour vous l'état pending, le séquencement, les erreurs (remontées à l'Error Boundary) et la réinitialisation du formulaire. Les hooks useActionState, useFormStatus et useOptimistic sont bâtis dessus.
Un serveur de restaurant qui prend la commande, annonce « c'est en cours », et revient vous prévenir si la cuisine a un souci : vous n'avez plus à surveiller la porte.
function UpdateName() {
const [name, setName] = useState('');
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
function handleSubmit() {
startTransition(async () => { // Action
const err = await updateName(name); // isPending géré par React
if (err) { setError(err); return; }
redirect('/profile');
});
}
return <button onClick={handleSubmit} disabled={isPending}>Enregistrer</button>;
}Cas d'usage : Toute mutation déclenchée par l'utilisateur (enregistrer, liker, supprimer) où l'on affichait auparavant un état isLoading manuel.
Fonctions de react-dom stables depuis React 19 (5 décembre 2024) qui indiquent au navigateur les ressources à venir : prefetchDNS(url), preconnect(url), preload(url, { as }) et preinit(url, { as }) (télécharge ET exécute un script ou applique une feuille de style). En SSR, React émet les balises correspondantes dans le <head> ; côté client, elles s'ajoutent dynamiquement.
Réserver la table et commander l'entrée avant d'arriver au restaurant : quand vous vous asseyez, tout est déjà en route.
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';
function ProductPage() {
preinit('/scripts/checkout.js', { as: 'script' }); // charge + exécute
preload('/fonts/inter.woff2', { as: 'font' }); // charge seulement
preconnect('https://cdn.images.example'); // handshake TLS
prefetchDNS('https://analytics.example');
return <Gallery />;
}Cas d'usage : Réduire la latence perçue d'une interaction probable (paiement, galerie) en anticipant les ressources dès le rendu du composant.
Depuis React 18, React regroupe automatiquement plusieurs mises à jour d'état en un seul re-rendu, y compris dans les callbacks asynchrones, les promesses et les timeouts. Ce comportement est acquis : la version stable actuelle est React 19.3 (9 septembre 2026). React 19.3 étend même le batching aux événements resize.
Comme un facteur qui attend d'avoir tout le courrier du quartier avant de faire sa tournée, au lieu d'un aller-retour par lettre.
// React 18: un seul re-rendu pour les deux
async function handleClick() {
const data = await fetchData();
setName(data.name); // batch
setAge(data.age); // batch -> 1 seul rendu
}Cas d'usage : Comportement par défaut de toute racine créée avec createRoot : aucune intervention du développeur, un seul rendu par lot de setState.
Nouvelle API de react-dom dans React 19.3 (9 septembre 2026). Appelée via use(browser(reason)), elle fait suspendre le composant pendant le rendu serveur (le fallback Suspense part dans le HTML) et ne fait rien dans le navigateur. C'est la réponse officielle au problème des composants dépendant de window ou localStorage en SSR, sans hack typeof window ni useEffect de montage.
Un panneau « ouverture à l'arrivée du personnel » : le serveur laisse la vitrine vide, le navigateur ouvre la boutique.
'use client';
import { use, useState, Suspense } from 'react';
import { browser } from 'react-dom';
function SavedDraft() {
use(browser('Le brouillon est dans localStorage.'));
const [draft, setDraft] = useState(() => localStorage.getItem('draft') ?? '');
return <textarea value={draft} onChange={e => setDraft(e.target.value)} />;
}
export default () => (
<Suspense fallback={<p>Chargement du brouillon…</p>}>
<SavedDraft />
</Suspense>
);Cas d'usage : Widgets fuseau horaire, thème stocké côté client, éditeurs riches non SSR-compatibles.
Analyse visuelle du contenu du bundle JavaScript pour identifier les dépendances lourdes et les opportunités de code splitting.
Comme une radio de valise a l'aéroport : tu vois exactement ce qui prend le plus de place dans ton bagage.
// Avec @next/bundle-analyzer
// next.config.js
const withAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withAnalyzer(nextConfig);
// ANALYZE=true npm run buildCas d'usage : Diagnostiquer pourquoi le bundle est trop gros et prioriser les optimisations de taille.
cache(fn) mémoïse une fonction pour la durée d'une requête serveur, uniquement dans les Server Components : deux composants qui appellent la même fonction avec les mêmes arguments partagent le résultat (dédoublonnage). Le cache est vidé à chaque requête. cacheSignal(), ajouté dans React 19.2 (1er octobre 2025), fournit un AbortSignal qui se déclenche quand la durée de vie du cache se termine, pour annuler les travaux inutiles.
Une commande groupée au restaurant : plusieurs convives demandent le même plat, la cuisine ne le prépare qu'une fois pour ce service.
// lib/data.js — définir UNE fois dans un module partagé
import { cache, cacheSignal } from 'react';
export const getUser = cache(async (id) => {
const res = await fetch(`/api/users/${id}`, { signal: cacheSignal() });
return res.json();
});
// Deux Server Components, un seul appel réseau par requête
async function Header({ id }) { const u = await getUser(id); /* ... */ }
async function Sidebar({ id }) { const u = await getUser(id); /* ... */ }Cas d'usage : Éviter les requêtes BDD dupliquées quand plusieurs Server Components d'une même page ont besoin de la même donnée.
Fonctions de next/cache qui pilotent une portée 'use cache' (Next.js 16, 21 octobre 2025). cacheLife(profil) fixe stale (client), revalidate (régénération en arrière-plan) et expire ; profils intégrés seconds, minutes, hours, days, weeks, max, default (5 min / 15 min / jamais). cacheTag(tag) étiquette l'entrée. Côté mutation : revalidateTag(tag, profil) pour du stale-while-revalidate, updateTag(tag) dans une Server Action pour relire immédiatement ses écritures, refresh() pour rafraîchir les données non cachées.
Les dates sur un emballage : « à consommer de préférence » (revalidate) et « à consommer jusqu'au » (expire), plus une étiquette (tag) pour retirer un lot du rayon.
import { cacheLife, cacheTag, updateTag } from 'next/cache';
export async function getPost(slug: string) {
'use cache';
cacheLife('days'); // stale 5 min, revalidate 1 j, expire 1 sem
cacheTag(`post-${slug}`);
return db.posts.find(slug);
}
'use server';
export async function savePost(slug: string, data: Post) {
await db.posts.update(slug, data);
updateTag(`post-${slug}`); // l'auteur relit sa modif immédiatement
// revalidateTag('posts', 'max') => SWR ; refresh() => données non cachées
}Cas d'usage : Blog ou catalogue : longue durée de vie + invalidation à la sauvegarde ; flux qui bouge dans la journée : profil hours ou minutes.
Technique qui découpe le bundle JS en morceaux charges a la demande via React.lazy et import() dynamique, réduisant le temps de chargement initial.
Comme un buffet a volonté : tu ne prends que ce que tu vas manger maintenant, pas tout le menu d'un coup.
const AdminPanel = React.lazy(() => import('./AdminPanel'));
function App() {
return (
<Suspense fallback={<Spinner />}>
{isAdmin && <AdminPanel />}
</Suspense>
);
}Cas d'usage : Pages lourdes (admin, dashboard), composants conditionels, routes non critiques.
Les différentes phases d'un composant : montage, mise a jour et démontage. En hooks, useEffect gère ces phases.
Comme la vie d'un employé : embauche (mount), promotions (update), départ (unmount).
useEffect(() => {
console.log('Monte ou mis a jour');
return () => console.log('Demonte / cleanup');
}, [dep]); // se relance si dep changeCas d'usage : Gérer les effets de bord comme les abonnements, les appels API ou les timers.
Pattern ou un composant parent partage son état implicitement avec ses enfants via Context, offrant une API déclarative et flexible.
Comme un menu déroulant HTML <select>/<option> : les options savent qu'elles font partie du select sans configuration explicite.
// Utilisation declarative
<Tabs>
<Tabs.List>
<Tabs.Tab>Profil</Tabs.Tab>
<Tabs.Tab>Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panels>
<Tabs.Panel>Contenu profil</Tabs.Panel>
<Tabs.Panel>Contenu settings</Tabs.Panel>
</Tabs.Panels>
</Tabs>Cas d'usage : Composants UI complexes (tabs, accordéons, menus) nécessitant une API flexible et composable.
Mode de rendu ou React peut interrompre, reprendre et abandonner des rendus en cours pour prioriser les interactions utilisateur urgentes.
Comme un serveur de restaurant qui interrompt la préparation d'une commande pour répondre a un client qui attend a la caisse.
// Active automatiquement avec createRoot
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);Cas d'usage : Garder l'interface réactivé pendant des rendus lourds (longues listes, calculs complexes).
Pattern qui sépare les composants en deux types : Container (logique, data fetching) et Presentational (UI pure, reçoit des props). Moins pertinent avec les hooks.
Comme un réalisateur (container) et un acteur (presentational) : l'un dirige, l'autre joue le role.
// Presentational : UI pure
function UserCard({ name, avatar }) {
return <div><img src={avatar} /><p>{name}</p></div>;
}
// Container : logique
function UserCardContainer({ userId }) {
const { data } = useQuery({ queryKey: ['user', userId], queryFn: fetchUser });
return <UserCard name={data?.name} avatar={data?.avatar} />;
}Cas d'usage : Rendre les composants UI réutilisables et testables en les séparant de la logique.
Partage de données sans prop drilling via createContext et un fournisseur. Depuis React 19 (5 décembre 2024), on rend directement <MonContext value={...}> au lieu de <MonContext.Provider>, et use(MonContext) peut remplacer useContext. Attention : chaque changement de valeur re-rend tous les consommateurs.
Comme un haut-parleur dans un magasin : tout le monde entend l'annonce, même ceux qui ne sont pas concernes.
const AuthCtx = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({ user, setUser }), [user]);
// React 19 : le Context est lui-même le fournisseur
return <AuthCtx value={value}>{children}</AuthCtx>;
}
function Avatar() {
const { user } = use(AuthCtx); // ou useContext(AuthCtx)
return <img src={user?.avatar} alt="" />;
}Cas d'usage : Données peu changeantes (theme, auth, locale) partagées a travers l'arbre.
Controlled : la valeur est gérée par React (via state + onChange). Uncontrolled : la valeur est gérée par le DOM (via ref et defaultValue).
Controlled = GPS qui te guide a chaque virage. Uncontrolled = carte papier que tu consultes quand tu veux.
// Controlled
const [val, setVal] = useState('');
<input value={val} onChange={e => setVal(e.target.value)} />
// Uncontrolled
const ref = useRef();
<input defaultValue="" ref={ref} />
// ref.current.value pour lireCas d'usage : Controlled pour validation en temps réel, uncontrolled pour formulaires simples ou performance critique.
Fonction RTK qui génère automatiquement un thunk avec les actions pending/fulfilled/rejected pour les opérations asynchrones.
Comme un livreur avec suivi : tu sais quand il part (pending), arrive (fulfilled) ou échoue (rejected).
const fetchUsers = createAsyncThunk(
'users/fetch',
async (_, { rejectWithValue }) => {
try { return (await api.getUsers()).data; }
catch (e) { return rejectWithValue(e.message); }
}
);Cas d'usage : Appels API avec gestion automatique des états de chargement et d'erreur dans Redux.
Fonction de Reselect (intégrée a RTK) qui crée des sélecteurs memoises. Le résultat n'est recalcule que si les inputs changent, évitant des calculs redondants.
Comme un formulaire pre-rempli : tant que les données source ne changent pas, le calcul dérive est réutilisé.
const selectUsers = (state) => state.users.list;
const selectActiveUsers = createSelector(
[selectUsers],
(users) => users.filter(u => u.active)
);
// Ne refiltre que si state.users.list changeCas d'usage : Dérivation de données coûteuses depuis le store Redux (filtrage, tri, aggregation).
Le serveur envoie un HTML vide avec un bundle JS. Le navigateur exécuté React pour générer le DOM. Mauvais pour le SEO et le premier affichage.
Comme recevoir des meubles en kit : le client (navigateur) doit tout assembler lui-même avant de pouvoir s'asseoir.
// index.html minimal
// <div id="root"></div>
// <script src="bundle.js"></script>
import { createRoot } from 'react-dom/client';
createRoot(document.getElementById('root')).render(<App />);Cas d'usage : Dashboards internes, apps behind auth ou le SEO n'est pas critique.
Fonction JavaScript prefixee par 'use' qui encapsulé de la logique réutilisable composée d'autres hooks. Chaque appel créé sa propre instance de state.
Comme une recette de cuisine : tu la réutilisés dans différents repas, mais chaque plat a ses propres ingrédients (state).
function useDebounce(value, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}Cas d'usage : Extraire et réutiliser de la logique stateful entre composants (fetch, debounce, form, auth).
Vulnérabilité critique (CVSS 10.0) divulguée le 3 décembre 2025 : une requête HTTP forgée vers un endpoint de Server Function pouvait, lors de la désérialisation par react-server-dom-webpack, -parcel ou -turbopack (versions 19.0, 19.1.0, 19.1.1, 19.2.0), exécuter du code sur le serveur sans authentification. Toute application RSC était exposée, même sans Server Function explicite. Correctifs : 19.0.1, 19.1.2, 19.2.1 ; Next.js 14.2.35, 15.0.8+, 16.0.11+. Un second avis (DoS et exposition de code source) a suivi le 11 décembre 2025.
Une boîte aux lettres qui exécutait ce qu'on y glissait : il fallait la remplacer, pas juste changer la serrure.
# Vérifier les paquets vulnérables installés
npm ls react-server-dom-webpack react-server-dom-turbopack react-server-dom-parcel
# Corriger : React >= 19.0.1 / 19.1.2 / 19.2.1 et le framework à jour
npm i react@latest react-dom@latest next@latest
# Bonnes pratiques durables
# - autorisation DANS chaque Server Function (pas seulement dans proxy.ts)
# - validation des arguments (zod) : tout ce qui arrive du client est hostile
# - alertes Dependabot/Renovate sur react-server-dom-*Cas d'usage : Réponse d'entretien sur la surface d'attaque des RSC ; check-list de mise à jour d'urgence dans une équipe qui exploite Next.js ou React Router.
Mode de test Cypress qui monte des composants React dans un vrai navigateur, permettant des tests visuels et d'interaction sans lancer toute l'application.
Comme tester une pièce de voiture sur un banc d'essai réel au lieu d'une simulation informatique.
import { mount } from 'cypress/react';
it('affiche le compteur', () => {
mount(<Counter initial={5} />);
cy.get('[data-testid="count"]').should('have.text', '5');
cy.get('button').click();
cy.get('[data-testid="count"]').should('have.text', '6');
});Cas d'usage : Tests d'interaction complexes (drag & drop, animations) nécessitant un vrai navigateur.
Depuis React 19 (5 décembre 2024), <title>, <meta> et <link> rendus n'importe où dans l'arbre sont hissés automatiquement dans le <head>. Les <link rel="stylesheet" precedence="..."> sont dédoublonnées, ordonnées par précédence et attendues avant l'affichage du composant qui en dépend ; les <script async> sont dédoublonnés. Cela réduit le besoin de react-helmet ou d'un système de styles maison en SSR/RSC.
Un courrier interne : vous déposez la note dans n'importe quel service, elle finit épinglée sur le tableau d'affichage de l'entrée.
function BlogPost({ post }) {
return (
<article>
<title>{post.title}</title> {/* hissé dans <head> */}
<meta name="author" content={post.author} />
<link rel="stylesheet" href="/article.css" precedence="default" />
<script async src="/embed.js" /> {/* dédoublonné */}
<h1>{post.title}</h1>
</article>
);
}Cas d'usage : SEO par page dans une app SSR/RSC, chargement de CSS par composant sans FOUC, scripts tiers embarqués une seule fois.
Utilitaire RTK qui génère un ensemble de reducers et sélecteurs pour gérer des collections normalisées (ids + entities) avec opérations CRUD pre-construites.
Comme un classeur a fiches avec index : ajout, suppression et recherche sont automatises et optimises.
const usersAdapter = createEntityAdapter();
const usersSlice = createSlice({
name: 'users',
initialState: usersAdapter.getInitialState(),
reducers: {
addUser: usersAdapter.addOne,
removeUser: usersAdapter.removeOne,
}
});Cas d'usage : Collections d'entités (utilisateurs, produits, commentaires) nécessitant des opérations CRUD normalisées.
Composant classe qui intercepte les erreurs de rendu de son sous-arbre et affiche une UI de secours ; il n'existe toujours pas d'équivalent hook. React 19 (5 décembre 2024) ajoute aux racines les options onCaughtError et onUncaughtError et ne relance plus les erreurs de rendu (elles vont à window.reportError ou console.error). Next.js 16.3 (3 août 2026) fournit catchError (next/error) : une frontière qui n'interfère pas avec notFound/redirect et expose retry() pour re-rendre des Server Components en échec.
Comme un fusible électrique : il saute pour protéger le reste du circuit.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) { logToService(error, info.componentStack); }
render() {
return this.state.hasError ? <p>Oups !</p> : this.props.children;
}
}
// React 19 : journalisation globale à la racine
createRoot(el, {
onCaughtError: (err, info) => logToService(err, info),
onUncaughtError: (err, info) => logToService(err, info),
}).render(<App />);Cas d'usage : Encapsuler des sections critiques pour que le reste de l'app continue de fonctionner en cas d'erreur.
Depuis React 19.2 (1er octobre 2025), eslint-plugin-react-hooks v6 utilise la flat config par défaut et embarque des règles issues du compilateur : set-state-in-render (boucles de rendu), set-state-in-effect (travail coûteux dans un effet), refs (lecture de ref pendant le rendu), en plus de rules-of-hooks et exhaustive-deps. Ces règles encodent les Rules of React que le compilateur exige.
Le contrôle technique avant la course : la voiture (le compilateur) ne part que si les voyants sont éteints.
// eslint.config.js (flat config)
import reactHooks from 'eslint-plugin-react-hooks';
export default [
reactHooks.configs.flat.recommended,
];
// Exemple d'erreur détectée : set-state-in-render
function Counter({ start }) {
const [n, setN] = useState(0);
if (n < start) setN(start); // ❌ setState pendant le rendu => boucle
return <p>{n}</p>;
}Cas d'usage : Préparer une base de code à React Compiler et attraper les bugs de pureté avant la production.
Create React App est officiellement déprécié depuis le 14 février 2025 : plus de mainteneurs actifs, pas de routage, de data fetching ni de code splitting intégrés. L'équipe React recommande un framework (Next.js, React Router en mode framework, Expo) ou, à défaut, un outil de build (Vite, Parcel, Rsbuild). Le paquet reste installable en mode maintenance avec un avertissement.
La vieille voiture de l'auto-école : elle roule encore, mais plus personne ne la répare et on n'apprend plus dessus.
# ❌ Déprécié depuis le 14/02/2025
npx create-react-app mon-app
# ✅ Framework (recommandé par l'équipe React)
npx create-next-app@latest mon-app
npx create-react-router@latest mon-app
# ✅ Outil de build seul (SPA)
npm create vite@latest mon-app -- --template react-tsCas d'usage : Question d'entretien : « comment démarres-tu un projet React aujourd'hui ? » ; migration d'un vieux projet CRA vers Vite ou un framework.
Conteneur invisible qui permet de retourner plusieurs éléments sans ajouter de noeud DOM supplémentaire.
Comme un trombone pour regrouper des feuilles : il les tient ensemble sans ajouter de page vide.
function Info() {
return (
<>
<h1>Titre</h1>
<p>Description</p>
</>
);
}Cas d'usage : Éviter les divs inutiles qui cassent le layout CSS ou la sémantique HTML.
Stable depuis React 19.3 (9 septembre 2026) : <Fragment ref={...}> (syntaxe explicite, pas <>) reçoit une FragmentInstance qui agit sur les nœuds DOM enfants de premier niveau sans élément englobant : addEventListener, focus/focusLast/blur, observeUsing (IntersectionObserver, ResizeObserver), getClientRects, scrollIntoView. Utile pour les composants qui ne veulent pas imposer de <div> wrapper.
Une télécommande universelle qui pilote plusieurs appareils posés côte à côte, sans avoir à les mettre dans la même armoire.
import { Fragment, useRef, useEffect } from 'react';
function Headings({ posts }) {
const ref = useRef(null);
useEffect(() => {
ref.current.focus(); // premier focusable
const obs = new IntersectionObserver(onSeen);
ref.current.observeUsing(obs); // enfants DOM de 1er niveau
return () => ref.current?.unobserveUsing(obs);
}, []);
return (
<Fragment ref={ref}> {/* syntaxe explicite, pas <> */}
{posts.map(p => <h2 key={p.id} tabIndex={0}>{p.title}</h2>)}
</Fragment>
);
}Cas d'usage : Gestion du focus et observation de visibilité dans des listes ou des composants headless sans wrapper qui casse la grille CSS.
Composants qui fournissent la logique et l'accessibilite sans imposer de style ni de markup. L'utilisateur contrôle entièrement le rendu.
Comme un moteur de voiture vendu sans carrosserie : tu choisis la forme, il fournit la puissance.
// Headless UI Combobox
<Combobox value={selected} onChange={setSelected}>
<Combobox.Input onChange={e => setQuery(e.target.value)} />
<Combobox.Options>
{filtered.map(item => (
<Combobox.Option key={item.id} value={item}>{item.name}</Combobox.Option>
))}
</Combobox.Options>
</Combobox>Cas d'usage : Design systems, librairies UI (Headless UI, Radix, Downshift) qui doivent être stylisables sans contrainte.
Fonction qui prend un composant et retourne un nouveau composant enrichi. Pattern classique de réutilisation de logique, largement remplace par les hooks.
Comme un cadre photo qui embellit toute photo qu'on y met : le HOC ajoute des fonctionnalités a tout composant.
function withAuth(Component) {
return function AuthWrapper(props) {
const { user } = useAuth();
if (!user) return <Redirect to="/login" />;
return <Component {...props} user={user} />;
};
}
const ProtectedPage = withAuth(Dashboard);Cas d'usage : Ajouter de la logique transverse (auth, logging, theming) a plusieurs composants.
Pattern TanStack Query pour le scroll infini ou le 'charger plus'. useInfiniteQuery gère automatiquement les pages, le curseur et la concatenation des résultats.
Comme un rouleau de papier toilette : tu en deroules plus quand tu en as besoin, page après page.
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
// data.pages contient toutes les pages chargeesCas d'usage : Feeds sociaux, listes de produits, tout contenu pagine avec scroll infini ou bouton 'charger plus'.
Permet d'intercepter une route pour afficher son contenu dans le layout actuel (ex: modale) au lieu de naviguer vers la page complète.
Comme un aperçu rapide au survol : tu vois le contenu sans quitter ta page, mais l'URL directe montre la version complète.
// app/@modal/(.)photo/[id]/page.tsx
// Le (.) intercepte /photo/[id]
export default function PhotoModal({ params }) {
return (
<Modal>
<Photo id={params.id} />
</Modal>
);
}
// URL directe /photo/123 -> page completeCas d'usage : Photos en modale (Instagram-like), aperçu de produit, détail de notification sans quitter le feed.
Méthode TanStack Query qui marque des requêtes en cache comme périmées, déclenchant un refetch automatique si elles sont actuellement utilisées.
Comme tirer la chasse d'eau du cache : les données périmées sont évacuées et de nouvelles arrivent.
const queryClient = useQueryClient();
// Invalider toutes les requetes 'users'
queryClient.invalidateQueries({ queryKey: ['users'] });
// Invalider un user specifique
queryClient.invalidateQueries({ queryKey: ['users', userId] });Cas d'usage : Après une mutation réussie, forcer le rechargement des données affectées dans le cache.
Combinaison SSG + SSR : la page est générée statiquement puis régénérée en arrière-plan après un délai (revalidate). Introduit par Next.js. Depuis Next.js 16 (21 octobre 2025), revalidateTag exige un profil cacheLife en second argument ('max', 'hours'…) pour un comportement stale-while-revalidate, et Next.js 16.3 (3 août 2026) permet de servir une coquille de chargement instantanée au premier visiteur d'une URL non pré-rendue, puis de la promouvoir en page pré-rendue.
Comme un menu du jour affiche a l'entrée : il est pre-imprime mais le serveur le remplace discrètement chaque jour.
// Next.js App Router : régénération toutes les heures
export const revalidate = 3600;
export default async function Products() {
const products = await getProducts();
return <ProductGrid products={products} />;
}
// Invalidation à la demande (Next.js 16 : profil obligatoire)
import { revalidateTag } from 'next/cache';
revalidateTag('products', 'max');Cas d'usage : Pages e-commerce, catalogues : contenu relativement stable mais qui doit se mettre a jour sans rebuild complet.
Gestion d'état atomique pour React. Chaque atome est une unité de state indépendante, composable et réactive, sans Provider obligatoire.
Comme des briques LEGO : chaque brique (atome) est indépendante et tu les assembles pour construire ton state.
const countAtom = atom(0);
const doubleAtom = atom((get) => get(countAtom) * 2);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}Cas d'usage : State atomique granulaire quand chaque composant n'a besoin que d'une fraction de l'état global.
Syntaxe qui permet d'écrire du HTML dans du JavaScript. Le compilateur le transforme en appels React.createElement().
C'est comme un moule a gâteau : tu dessines la forme en HTML, et React coule le JavaScript dedans.
// JSX compile en createElement
const el = <h1 className="title">Bonjour</h1>;
// equivalent a :
const el2 = React.createElement('h1', { className: 'title' }, 'Bonjour');Cas d'usage : Utilise dans chaque composant React pour décrire l'interface de manière déclarative.
Attribut spécial qui aide React a identifier chaque élément dans une liste. Permet une réconciliation efficace sans reconstruire tout le DOM.
Comme les numéros de dossards dans une course : React sait qui est qui même si l'ordre change.
// Correct: identifiant unique
items.map(item => <li key={item.id}>{item.name}</li>)
// Anti-pattern: index comme key
items.map((item, i) => <li key={i}>{item.name}</li>)Cas d'usage : Obligatoire dans toute liste dynamique pour éviter des bugs de rendu et améliorer les performances.
Fonction associée à une route qui charge les données avant le rendu du composant (modes data et framework de React Router). En mode framework (depuis la v7, 22 novembre 2024), le composant reçoit loaderData typé en prop via les types générés Route.LoaderArgs et Route.ComponentProps.
Comme un serveur qui prépare ta table avant que tu arrives : tout est prêt quand tu t'assois.
// app/routes/user.tsx (framework mode, v7+)
import type { Route } from './+types/user';
export async function loader({ params }: Route.LoaderArgs) {
const user = await getUser(params.id);
return { user }; // plus besoin de json()
}
export default function User({ loaderData }: Route.ComponentProps) {
return <p>{loaderData.user.name}</p>; // typé automatiquement
}
// Mode data : useLoaderData() reste disponibleCas d'usage : Pre-charger les données d'une page avant le rendu pour éviter les waterfalls de fetching.
Fonctions exécutées autour des loaders et actions d'une route, activées par défaut depuis React Router v8 (17 juin 2026, plus de flag future.v8_middleware) en modes data et framework. Le middleware serveur reçoit { request, context } et next(), s'enchaîne parent → enfant puis remonte, et doit retourner une Response. Le contexte est typé via createContext / context.set / context.get ; un clientMiddleware existe pour le navigateur.
Les sas successifs d'un bâtiment sécurisé : chaque porte vérifie quelque chose avant de laisser passer vers la salle, et on ressort par les mêmes portes.
import { createContext, redirect } from 'react-router';
import type { Route } from './+types/dashboard';
export const userContext = createContext<User | null>(null);
async function auth({ request, context }: Route.MiddlewareArgs, next) {
const user = await getUserFromSession(request);
if (!user) throw redirect('/login');
context.set(userContext, user);
return next(); // Response obligatoire côté serveur
}
export const middleware: Route.MiddlewareFunction[] = [auth];
export async function loader({ context }: Route.LoaderArgs) {
return { user: context.get(userContext) }; // garanti non nul
}Cas d'usage : Authentification et autorisation par segment de route, journalisation, injection de dépendances (client BDD) dans les loaders.
Outil qui intercepte les requêtes réseau au niveau du Service Worker pour les mocker, sans modifier le code applicatif. Fonctionne en test et en dev.
Comme un doubleur de voix au cinéma : le vrai acteur (API) est absent mais la scène (test) se joue quand même.
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
http.get('/api/users', () => HttpResponse.json([
{ id: 1, name: 'Alice' }
]))
);
beforeAll(() => server.listen());Cas d'usage : Mocker les API dans les tests et pendant le développement frontend sans backend disponible.
Opération d'écriture (POST, PUT, DELETE) gérée par useMutation avec callbacks onSuccess/onError et possibilité de mises a jour optimistes.
Comme un formulaire de La Poste : tu le remplis (mutate), tu sais si c'est envoyé (onSuccess) ou perdu (onError).
const mutation = useMutation({
mutationFn: (newUser) => api.post('/users', newUser),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
mutation.mutate({ name: 'Alice' });Cas d'usage : Toute opération d'écriture vers le serveur avec invalidation automatique du cache après succès.
Routes imbriquées ou les routes enfants s'affichent a l'intérieur du layout de leur route parent via Outlet, permettant des layouts partages et une navigation hiérarchique.
Comme des poupées russes : chaque layout contient le suivant, et seul le contenu le plus interne change.
// Mode data (v7/v8) : `Component` remplace `element`
// /dashboard -> DashLayout + DashHome
// /dashboard/settings -> DashLayout + Settings
const router = createBrowserRouter([
{
path: 'dashboard',
Component: DashLayout,
children: [
{ index: true, Component: DashHome },
{ path: 'settings', Component: Settings },
],
},
]);Cas d'usage : Applications avec des layouts hiérarchiques (sidebar fixe, breadcrumbs dynamiques, multi-step wizards).
Système de routage Next.js fondé sur le système de fichiers, avec Server Components par défaut, layouts imbriqués et conventions (page, layout, loading, error). Version stable actuelle : Next.js 16.3 (3 août 2026) ; Next.js 16 (21 octobre 2025) a rendu Turbopack bundler par défaut, exige Node.js 20.9+, impose l'accès asynchrone à params, searchParams, cookies() et headers(), et exige un fichier default.js pour chaque slot de route parallèle.
Comme un immeuble ou chaque dossier est un étage et chaque fichier est une pièce avec un role précis.
// app/users/[id]/page.tsx
export default async function UserPage({ params }) {
const { id } = await params;
const user = await getUser(id);
return <h1>{user.name}</h1>;
}
// app/users/[id]/loading.tsx -> Suspense automatique
// app/users/[id]/error.tsx -> Error Boundary autoCas d'usage : Applications React full-stack avec SSR, RSC, routing et API integres.
Code exécuté avant le rendu d'une route pour rediriger, réécrire l'URL, poser des en-têtes ou des cookies. Depuis Next.js 16 (21 octobre 2025), le fichier middleware.ts est déprécié et renommé proxy.ts ; il s'exécute par défaut sur le runtime Node.js et non plus Edge.
Comme un vigile a l'entrée d'un bâtiment : il vérifie ton badge avant de te laisser entrer dans n'importe quelle pièce.
// proxy.ts (racine du projet) — Next.js 16+
import { NextResponse, type NextRequest } from 'next/server';
export function proxy(request: NextRequest) {
const token = request.cookies.get('token');
if (!token) return NextResponse.redirect(new URL('/login', request.url));
return NextResponse.next();
}
export const config = { matcher: ['/dashboard/:path*'] };
// Migration : npx @next/codemod@canary middleware-to-proxy .Cas d'usage : Redirections, réécritures, en-têtes CORS, vérification légère de session sur un groupe de routes via matcher.
Pattern combinant useOptimistic et useFormStatus pour afficher immédiatement le résultat d'une soumission de formulaire avant la confirmation serveur.
Comme un tchat ou ton message s'affiche instantanément avec une horloge, puis la coche de confirmation arrive du serveur.
function TodoForm({ addTodo }) {
const [optimistic, setOptimistic] = useOptimistic(todos);
async function action(formData) {
setOptimistic(prev => [...prev, { text: formData.get('text'), pending: true }]);
await addTodo(formData);
}
return <form action={action}><input name="text" /><SubmitBtn /></form>;
}Cas d'usage : Formulaires ou la latence perçue nuit a l'UX : chat, commentaires, listes de taches.
Technique qui met a jour l'UI immédiatement avant la confirmation serveur, puis annule en cas d'échec. Amélioré la perception de vitesse.
Comme célébrer un but avant la validation VAR : si le but est annule, on revient a l'état précédent.
useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: ['todos'] });
const prev = queryClient.getQueryData(['todos']);
queryClient.setQueryData(['todos'], old => [...old, newTodo]);
return { prev };
},
onError: (_, __, ctx) => queryClient.setQueryData(['todos'], ctx.prev),
});Cas d'usage : Actions utilisateur fréquentes (like, toggle, reorder) ou la latence réseau dégrade l'UX.
Composant React Router qui rend la route enfant correspondante dans un layout parent, permettant des layouts imbriques partageant du UI commun.
Comme un cadre photo dans un salon : le salon (layout) reste, mais la photo (enfant) change selon la page.
import { Outlet } from 'react-router'; // v7+/v8 : plus de react-router-dom
function Layout() {
return (
<div>
<Navbar />
<main><Outlet /></main> {/* la route enfant se rend ici */}
<Footer />
</div>
);
}Cas d'usage : Layouts partages (sidebar, header, footer) avec du contenu qui change selon la route active.
captureOwnerStack() (développement uniquement, retourne null en production) renvoie la pile des composants qui ont créé l'élément courant, et non de ceux qui l'ont simplement rendu ou transmis via children. Plus précise qu'une component stack pour remonter à l'origine d'une erreur ; disponible dans le rendu, les effets, les gestionnaires d'événements React et les callbacks d'erreur de la racine.
La chaîne de responsabilité d'un colis : qui l'a expédié, pas la liste des entrepôts traversés.
import * as React from 'react';
const original = console.error;
console.error = (...args) => {
original(...args);
if (process.env.NODE_ENV !== 'production') {
const owners = React.captureOwnerStack(); // null hors contexte React
if (owners) sendToOverlay({ message: args[0], owners });
}
};
// Component Stack : SubComponent > fieldset > Component > App
// Owner Stack : Component (celui qui a créé <SubComponent/>)Cas d'usage : Outils de développement, overlays d'erreur de framework, debug d'un composant rendu à des endroits multiples.
Fonctionnalité Next.js App Router qui permet de rendre plusieurs pages simultanément dans le même layout via des slots nommes (@slot).
Comme un écran divise en deux sur ta TV : tu regardes deux chaînés en même temps dans le même cadre.
// app/layout.tsx
export default function Layout({ children, analytics, team }) {
return (
<div>
{children}
{analytics} {/* @analytics/page.tsx */}
{team} {/* @team/page.tsx */}
</div>
);
}Cas d'usage : Dashboards avec plusieurs panneaux indépendants, modales comme routes, vues conditionnelles.
Rendu qui combine une coquille statique servie depuis le CDN et des « trous » dynamiques remplis en streaming via Suspense. Dans Next.js 16 (21 octobre 2025), le flag expérimental.ppr a été supprimé : le PPR est intégré au modèle « Cache Components » (cacheComponents: true, directive 'use cache'). Côté React, les API prerender / résume / resumeAndPrerender de react-dom (React 19.2, 1er octobre 2025) fournissent la brique bas niveau.
Comme un journal avec des encarts pub personnalises : le journal est imprime d'avance, mais les pubs changent par lecteur.
// next.config.ts
export default { cacheComponents: true };
// app/product/[id]/page.tsx
export default async function Page({ params }) {
const { id } = await params;
return (
<>
<ProductInfo id={id} /> {/* 'use cache' => coquille statique */}
<Suspense fallback={<CartSkeleton />}>
<Cart /> {/* dynamique, streamé */}
</Suspense>
</>
);
}Cas d'usage : Page e-commerce : en-tête, navigation et fiche produit pré-rendus ; panier et prix personnalisés streamés à la requête.
Option TanStack Query qui fournit des données temporaires pendant le chargement initial, sans les stocker dans le cache. Évite le flash de loading.
Comme une maquette de vitrine : elle montre la disposition avant que les vrais produits arrivent.
useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
placeholderData: (previousData) => previousData,
// ou: placeholderData: { name: 'Chargement...', email: '' }
});Cas d'usage : Afficher les données précédentés pendant une transition entre pages ou profils utilisateur.
Composants qui peuvent changer leur élément HTML racine via une prop 'as' ou 'component', tout en conservant un typage TypeScript correct.
Comme un costume transformable : le même vêtement peut devenir veste, gilet ou manteau selon le bouton presse.
function Box<C extends React.ElementType = 'div'>(
{ as, children, ...props }: { as?: C } & React.ComponentPropsWithoutRef<C>
) {
const Component = as || 'div';
return <Component {...props}>{children}</Component>;
}
<Box as="section">...</Box>
<Box as="a" href="/">Lien</Box>Cas d'usage : Design systems ou un même composant stylise doit s'adapter a différents éléments HTML sémantiques.
Permet de rendre un composant enfant dans un noeud DOM situe en dehors de la hiérarchie du parent. Les evenements remontent toujours dans l'arbre React.
Comme un tunnel secret : le composant vit dans la maison React mais sort par une porte cachée dans un autre endroit du DOM.
import { createPortal } from 'react-dom';
function Modal({ children }) {
return createPortal(
<div className="modal">{children}</div>,
document.getElementById('modal-root')
);
}Cas d'usage : Modales, tooltips, menus déroulants qui doivent échapper au overflow:hidden du parent.
Méthode de TanStack Query pour pre-charger des données en cache avant qu'elles ne soient nécessaires, éliminant le temps de chargement visible.
Comme préchauffer le four avant d'enfourner : quand tu en as besoin, tout est déjà prêt.
// Prefetch au hover d'un lien
<Link
onMouseEnter={() => {
queryClient.prefetchQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
}}
/>Cas d'usage : Pre-charger les données de la prochaine page au survol d'un lien ou pendant un formulaire multi-étapes.
prerender() (React 19, 5 décembre 2024) génère du HTML statique en attendant la résolution de toutes les frontières Suspense, et retourne { prélude, postponed }. Depuis React 19.2 (1er octobre 2025), résume() reprend un pré-rendu interrompu (via AbortSignal) pour terminer le rendu à la requête, et resumeAndPrerender() reprend vers du statique : c'est la brique React du Partial Prerendering. Ces API fonctionnent en Web Streams, y compris sous Node.js depuis 19.2.
Une pâte à pizza précuite : la base est prête à l'avance, on n'ajoute la garniture fraîche qu'à la commande.
import { prerender } from 'react-dom/static';
import { resume } from 'react-dom/server';
// Au build : coquille statique, arrêt après 10 s
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 10_000);
const { prelude, postponed } = await prerender(<App />, {
signal: ctrl.signal, bootstrapScripts: ['/main.js'],
});
await saveToCDN(prelude, postponed);
// À la requête : on reprend là où le pré-rendu s'est arrêté
const stream = await resume(<App />, postponed);Cas d'usage : SSG classique (prerender seul) ou PPR maison hors Next.js ; comprendre ce que Next.js Cache Components fait sous le capot.
Composant React et onglet DevTools qui mesurent le coût de rendu d'un sous-arbre : fréquence, durée et raison des re-rendus.
Comme un chronomètre pour chaque coureur dans un relais : tu identifies qui ralentit l'équipe.
<Profiler id="Navigation" onRender={(id, phase, duration) => {
console.log(`${id} ${phase}: ${duration.toFixed(1)}ms`);
}}>
<Nav items={items} />
</Profiler>Cas d'usage : Identifier les composants les plus lents pour cibler les optimisations de performance.
Principe ou l'application fonctionne sans JavaScript (formulaires HTML natifs, liens) puis s'améliore progressivement avec JS pour une meilleure UX.
Comme un escalier avec un escalator a cote : ca marche a pied (sans JS), mais c'est mieux avec le moteur (JS).
// Le formulaire fonctionne meme sans JS
<form action={serverAction} method="POST">
<input name="email" required />
<SubmitButton /> {/* desactive pendant l'envoi si JS actif */}
</form>Cas d'usage : Applications critiques (e-commerce, admin) qui doivent rester fonctionnelles même si le JS échoué.
Pattern qui fournit des fonctions (getInputProps, getToggleProps) retournant les props nécessaires pour un élément, fusionnant les props utilisateur avec celles du composant.
Comme un styliste qui te donne un ensemble complet : tu peux ajouter tes accessoires, il gère l'harmonie du tout.
function useSelect() {
const [isOpen, setOpen] = useState(false);
const getToggleProps = (userProps = {}) => ({
'aria-expanded': isOpen,
onClick: () => { userProps.onClick?.(); setOpen(!isOpen); },
...userProps,
});
return { isOpen, getToggleProps };
}
<button {...getToggleProps({ className: 'btn' })}>Toggle</button>Cas d'usage : Composants headless et librairies qui doivent gérer l'accessibilite tout en restant flexibles.
Données passées d'un composant parent a un composant enfant, en lecture seule. C'est le mécanisme principal de communication descendante.
Comme les ingrédients donnes a un cuisinier : il les utilise mais ne les modifie pas pour le fournisseur.
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
}
<Card title="Bienvenue">Contenu ici</Card>Cas d'usage : Rendre un composant réutilisable en parametrant son comportement et son contenu.
Pattern qui utilise Context.Provider pour fournir des données ou des services a un sous-arbre de composants, sans prop drilling.
Comme le Wi-Fi d'un bâtiment : tous les appareils dans la zone y accèdent sans câble direct.
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Usage: <ThemeProvider><App /></ThemeProvider>Cas d'usage : Fournir des services globaux (theme, i18n, auth, feature flags) a toute l'application.
Depuis Next.js 16 (21 octobre 2025), le fichier middleware.ts est renommé proxy.ts et sa fonction exportée proxy ; il s'exécute par défaut sur Node.js. Il intercepte les requêtes avant le rendu pour rediriger, réécrire, poser des en-têtes ou cookies, filtré par un matcher. Le renommage vise à clarifier son rôle de frontière réseau et à décourager son usage pour de la logique métier.
Le portier d'immeuble : il oriente et vérifie le badge à l'entrée, mais ne fait pas le travail des bureaux.
// proxy.ts
import { NextResponse, type NextRequest } from 'next/server';
export function proxy(request: NextRequest) {
if (!request.cookies.has('session')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
// Codemod : npx @next/codemod@canary middleware-to-proxy .Cas d'usage : Redirections de session, en-têtes CORS, A/B testing par cookie, réécritures d'URL legacy.
Identifiant unique d'une requête dans TanStack Query, utilise pour le cache, la deduplication et l'invalidation. Généralement un tableau hiérarchique.
Comme l'étiquette d'un tiroir de rangement : elle dit exactement ce qu'il y a dedans et permet de le retrouver.
// Hierarchique pour une invalidation fine
useQuery({ queryKey: ['users'] });
useQuery({ queryKey: ['users', userId] });
useQuery({ queryKey: ['users', userId, 'posts'] });
// Invalider tout ce qui commence par 'users'
queryClient.invalidateQueries({ queryKey: ['users'] });Cas d'usage : Structurer les clés de façon hiérarchique pour pouvoir invalider a différents niveaux de granularite.
Compilateur de build (plugin Babel babel-plugin-react-compiler) stable en version 1.0 depuis le 7 octobre 2025. Il analyse le flux de données de chaque composant et hook et insère la mémoïsation automatiquement, rendant useMemo, useCallback et React.memo optionnels. Il cible React 19 mais supporte React 17 et 18 via react-compiler-runtime. Les composants qui violent les Rules of React sont simplement laissés non compilés.
Un correcteur orthographique qui réécrit vos phrases lourdes à la volée, mais laisse tranquilles celles qu'il ne comprend pas.
// npm i -D babel-plugin-react-compiler@latest eslint-plugin-react-hooks@latest
// babel.config.js — doit être le PREMIER plugin
module.exports = { plugins: ['babel-plugin-react-compiler'] };
// next.config.ts (Next.js 16 : option stable)
export default { reactCompiler: true };
// Exclure un composant problématique
function Legacy() {
'use no memo';
// ...
}
// Vérification : badge « Memo ✨ » dans React DevToolsCas d'usage : Supprimer la mémoïsation manuelle d'une base de code et ses bugs de dépendances ; Meta rapporte jusqu'à 12 % de chargement initial en moins et des interactions 2,5× plus rapides.
Architecture interne de React (depuis v16) qui découpe le rendu en unités de travail interruptibles. Permet le rendu concurrent et la priorisation des mises a jour.
Comme un chef cuisinier qui peut interrompre la préparation d'un plat pour servir un client urgent, puis reprendre ou il en était.
// Fiber permet les priorites
// Haute priorite: saisie utilisateur
// Basse priorite: mise a jour d'une liste
const [query, setQuery] = useState('');
const [results, setResults] = useTransition();Cas d'usage : Fondation du concurrent rendering, permet a React de rester réactif même avec des rendus lourds.
Librairie de formulaires performante basée sur les refs (uncontrolled). Minimise les re-rendus et offre une validation intégrée avec support de Zod, Yup, etc.
Comme un formulaire papier : tu remplis tout d'un coup et la validation se fait a la soumission, pas a chaque lettre.
const { register, handleSubmit, formState: { errors } } = useForm();
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email', { required: true })} />
{errors.email && <span>Email requis</span>}
<button type="submit">Envoyer</button>
</form>Cas d'usage : Formulaires complexes avec beaucoup de champs ou la performance est critique.
Depuis React 19.2 (1er octobre 2025), React ajoute ses propres pistes dans l'onglet Performance de Chrome DevTools : une piste Scheduler (priorité des travaux : blocking, transition, idle) et une piste Components (durée de rendu et d'effets par composant). Cela complète le Profiler React en situant le travail React dans la chronologie globale du navigateur (layout, peinture, réseau).
Ajouter les voies « React » sur le plan du métro : on voit enfin où le train React s'arrête par rapport aux autres lignes.
# 1. Application en mode développement (ou build avec profiling)
npm run dev
# 2. Chrome DevTools > Performance > Enregistrer > interagir > Stop
# 3. Pistes « Scheduler ⚛ » et « Components ⚛ » :
# - Scheduler : Blocking / Transition / Idle, mises à jour et leur cause
# - Components : Mount, Update, Effects par composant (durée, cascade)
# Astuce : nommer les transitions avec addTransitionType pour les retrouverCas d'usage : Diagnostiquer un rendu long ou une cascade d'effets, vérifier qu'une mise à jour lourde est bien en priorité Transition.
Mode le plus complet de React Router, hérité de Remix lors de la fusion dans la v7 (22 novembre 2024) et pleinement stable en v8 (17 juin 2026, dernière 8.4.0 le 15 septembre 2026). Il ajoute au mode data l'intégration Vite : routes.ts, modules de route typés (Route.LoaderArgs, Route.ComponentProps, href typé), découpage automatique, SSR, pré-rendu statique ou SPA. Le support des Server Components y reste expérimental (RSCHydratedRouter, routeRSCServerRequest, via @vitejs/plugin-rsc).
Le même vélo (le routeur), mais livré avec cadre, freins et éclairage montés : prêt à rouler sur route ouverte.
// app/routes.ts
import { index, route } from '@react-router/dev/routes';
export default [index('./home.tsx'), route('products/:pid', './product.tsx')];
// app/product.tsx — module de route
import type { Route } from './+types/product';
export async function loader({ params }: Route.LoaderArgs) {
return { product: await getProduct(params.pid) };
}
export default function Product({ loaderData }: Route.ComponentProps) {
return <h1>{loaderData.product.name}</h1>;
}Cas d'usage : Application full-stack React sans Next.js, déployable sur Node, Docker ou Cloudflare Workers ; migration d'un projet Remix v2.
Librairie de routage de référence pour React. La v6 (routes imbriquées, loaders, actions) reste maintenue (6.30.6 le 18 août 2026), mais la v7 (22 novembre 2024) a fusionné Remix dans React Router et ajouté le « framework mode » ; la v8 (17 juin 2026, dernière 8.4.0 le 15 septembre 2026) est ESM uniquement, supprime react-router-dom au profit du seul paquet react-router, et exige React 19.2.7+.
Comme le plan d'un centre commercial avec les flèches directionnelles : chaque URL mène a la bonne boutique (composant).
// v7/v8 : un seul paquet, `Component` remplace `element`
import { createBrowserRouter, RouterProvider } from 'react-router';
const router = createBrowserRouter([
{
path: '/',
Component: Layout,
children: [
{ index: true, Component: Home },
{ path: 'users/:id', Component: User, loader: userLoader },
],
},
]);
createRoot(root).render(<RouterProvider router={router} />);Cas d'usage : SPA Vite + React en mode déclaratif ou data ; application full-stack (SSR, pré-rendu, typage des routes) en mode framework, alternative directe à Next.js.
Composants exécutés uniquement sur le serveur (accès direct BDD, fichiers, secrets), dont le code n'est jamais envoyé au navigateur. Ils sont stables dans React 19 (5 décembre 2024) mais nécessitent un framework ou bundler (Next.js, React Router, Parcel, Vite via @vitejs/plugin-rsc) : les API internes de bundling ne suivent pas semver entre versions mineures. Depuis React 19.3 (9 septembre 2026), un <Context value> peut être rendu directement dans un Server Component.
Comme la cuisine d'un restaurant : le client ne voit que le plat (HTML), pas les ustensiles (code serveur).
// Server Component (par defaut dans Next.js App Router)
export default async function UserList() {
const users = await db.query('SELECT * FROM users');
return (
<ul>
{users.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
);
}Cas d'usage : Composants qui affichent des données sans interactivité : listes, articles, dashboards de lecture.
Librairie de test qui encourage les tests du point de vue de l'utilisateur. On interagit via roles, labels et texte visible, pas via les détails d'implémentation.
Comme un testeur mystère dans un restaurant : il teste l'expérience client, pas la recette du chef.
import { render, screen, fireEvent } from '@testing-library/react';
test('incremente le compteur', () => {
render(<Counter />);
fireEvent.click(screen.getByRole('button', { name: /incrementer/i }));
expect(screen.getByText('1')).toBeInTheDocument();
});Cas d'usage : Tests unitaires et d'intégration de composants React, focus sur le comportement utilisateur.
HOC qui memoize un composant : il ne se re-rend que si ses props changent (comparaison shallow par défaut).
Comme un videur de boite de nuit qui ne laisse passer que les nouvelles têtes : mêmes props = on ne re-rend pas.
const ExpensiveList = React.memo(function List({ items }) {
return items.map(i => <Item key={i.id} {...i} />);
});
// Avec comparateur custom
const MemoComp = React.memo(Comp, (prev, next) => {
return prev.id === next.id;
});Cas d'usage : Composant enfant coûteux re-rendu inutilement à cause du parent. Depuis React Compiler 1.0 (7 octobre 2025), la mémoïsation est automatique dans les projets compilés : React.memo devient une soupape manuelle, utile surtout hors compilateur ou pour un contrôle explicite.
Algorithme de diffing qui compare deux arbres Virtual DOM pour déterminer les changements minimaux a appliquer au vrai DOM. Complexité O(n) grâce a des heuristiques.
Comme un jeu des 7 différences : React compare les deux images et ne corrige que les pixels différents.
// React compare par type d'element et key
// Si le type change: detruit et recree le sous-arbre
// Si le type est identique: met a jour les props
<ul>
<li key="a">A</li> {/* key aide la reconciliation */}
<li key="b">B</li>
</ul>Cas d'usage : Comprendre la réconciliation aide a écrire du code performant et a bien utiliser les keys.
Boite a outils officielle de Redux qui simplifie la configuration du store, la création de reducers et la gestion de la logique asynchrone.
Comme un kit de montage IKEA pour Redux : tout est pre-configure, plus besoin de 200 fichiers boilerplate.
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
add: (state, action) => { state.value += action.payload; }
}
});Cas d'usage : État global complexe nécessitant de la predictibilite, du time-travel debugging et du middleware.
Depuis React 19 (5 décembre 2024), un composant fonction reçoit ref comme une prop ordinaire : plus besoin de forwardRef, que la doc officielle annonce déprécié dans une version future. Un codemod est fourni. En complément, un callback ref peut retourner une fonction de nettoyage appelée au démontage.
Avant, il fallait passer par un guichet spécial (forwardRef) pour transmettre la clé ; maintenant la clé arrive dans le même courrier que le reste.
// Avant (React 18)
const Input = forwardRef((props, ref) => <input {...props} ref={ref} />);
// Après (React 19)
function Input({ ref, ...props }) {
return <input {...props} ref={ref} />;
}
// Callback ref avec nettoyage (React 19)
<div ref={(node) => {
const obs = new ResizeObserver(onResize);
if (node) obs.observe(node);
return () => obs.disconnect(); // appelé au démontage
}} />Cas d'usage : Composants de design system (Input, Button) qui exposent leur nœud DOM au parent ; abonnements DOM avec nettoyage sans useEffect.
Conventions issues de Remix : routes = fichiers, loader, action et composant co-localisés, standards web (fetch, FormData, Response). Depuis React Router v7 (22 novembre 2024), Remix a été fusionné dans React Router : ces conventions vivent désormais dans le « framework mode » de React Router, stable et par défaut en v8 (17 juin 2026).
Comme un restaurant local bio : il utilise les ingrédients natifs (standards web) au lieu d'importer des produits exotiques.
// app/routes.ts : configuration explicite des routes (v7+)
import { index, route } from '@react-router/dev/routes';
export default [
index('./home.tsx'),
route('users/:id', './user.tsx'),
];
// app/user.tsx : loader + action + composant co-localisés
export async function loader({ params }: Route.LoaderArgs) {
return { user: await getUser(params.id) };
}
export default function User({ loaderData }: Route.ComponentProps) {
return <h1>{loaderData.user.name}</h1>;
}Cas d'usage : Migrer une application Remix v2 vers React Router v7/v8 (framework mode) ; démarrer un projet full-stack React sans Next.js.
Pattern ou un composant reçoit une fonction en prop qui retourne du JSX, permettant de partager de la logique tout en laissant le contrôle du rendu a l'appelant.
Comme un cours de peinture 'apporte ta toile' : le professeur (composant) guide, mais tu décides du résultat.
function MouseTracker({ render }) {
const [pos, setPos] = useState({ x: 0, y: 0 });
return (
<div onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>
{render(pos)}
</div>
);
}
<MouseTracker render={({ x, y }) => <p>{x}, {y}</p>} />Cas d'usage : Partager de la logique réutilisable entre composants (souvent remplace par les custom hooks aujourd'hui).
Dossiers parenthèses (group) dans App Router qui organisent les routes sans affecter l'URL. Permettent des layouts différents pour des sections de l'app.
Comme des dossiers de classement invisibles : ils organisent les papiers mais ne changent pas l'adresse postale.
// app/(marketing)/about/page.tsx -> /about
// app/(marketing)/layout.tsx -> layout marketing
// app/(dashboard)/admin/page.tsx -> /admin
// app/(dashboard)/layout.tsx -> layout dashboardCas d'usage : Séparer les layouts (public vs dashboard) ou organiser le code sans impacter les URLs.
Format streamé que le serveur envoie au client pour décrire l'arbre rendu par les Server Components : HTML rendu, références aux Client Components à charger, props sérialisées et Promises encore en cours. Seules les valeurs sérialisables traversent la frontière : primitives, objets simples, Array/Map/Set/Date, Promises, éléments JSX et Server Functions. Fonctions ordinaires, instances de classes et symboles locaux sont refusés. 'use client' marque une frontière de module, pas de composant.
Une notice de montage IKEA : le meuble (HTML) déjà assemblé, plus la liste des pièces à visser sur place (Client Components) et leurs vis (props).
// Server Component
export default async function Page() {
const user = await db.users.get(1); // objet simple : OK
const postsPromise = db.posts.byUser(1); // Promise : OK, streamée
return (
<Profile
user={user}
posts={postsPromise}
onSave={saveProfile} // Server Function ('use server') : OK
// formatter={new Intl.DateTimeFormat()} ❌ instance de classe
/>
);
}Cas d'usage : Comprendre pourquoi une prop « ne passe pas » à un Client Component, et pourquoi le code serveur (BDD, secrets) n'atteint jamais le navigateur.
Solution de data fetching et caching intégrée a Redux Toolkit. Génère automatiquement les hooks, gère le cache, l'invalidation et les requêtes optimistes.
Comme un assistant personnel qui passe les commandes, gère le stock et prévient quand il faut réapprovisionner.
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (build) => ({
getUsers: build.query({ query: () => '/users' }),
addUser: build.mutation({ query: (body) => ({ url: '/users', method: 'POST', body }) })
})
});Cas d'usage : Applications Redux avec beaucoup de data fetching, en alternative a React Query dans l'écosystème Redux.
Fonctions asynchrones exécutées sur le serveur et appelables depuis un composant client, typiquement via la prop action d'un <form>. Depuis septembre 2024, React les appelle « Server Functions » ; « Server Action » désigne le cas particulier d'une Server Function passée à une prop action. Stables dans React 19 (5 décembre 2024).
Comme commander par téléphone : tu passes l'ordre (client) et la cuisine (serveur) exécuté, sans aller au comptoir (API route).
// actions.ts
'use server';
import { updateTag } from 'next/cache';
export async function addTodo(formData: FormData) {
const session = await getSession(); // autorisation ICI
if (!session) throw new Error('Non autorisé');
await db.todos.create({ text: String(formData.get('text')) });
updateTag('todos'); // read-your-writes
}
// component.tsx
<form action={addTodo}><input name="text" /><button>Ajouter</button></form>Cas d'usage : Soumission de formulaire, CRUD, toute mutation serveur, avec revalidation du cache (Next.js 16 : updateTag pour relire immédiatement ses propres écritures, refresh pour les données non mises en cache).
Fonctions asynchrones marquées 'use server' qui s'exécutent sur le serveur et dont le client reçoit une référence sérialisée. Nom officiel depuis septembre 2024 ; « Server Action » désigne le cas où la fonction est passée à une prop action. Appelables via <form action>, useActionState ou startTransition. Stables dans React 19 (5 décembre 2024) ; les arguments et retours doivent être sérialisables.
Un bouton d'appel d'ascenseur : vous appuyez côté client, la machinerie côté serveur fait le travail, vous n'avez que le bouton.
// actions.js
'use server';
export async function createNote(title) {
await requireUser(); // autorisation obligatoire
return db.notes.create({ title }); // retour sérialisable
}
// NoteForm.jsx
'use client';
import { createNote } from './actions';
function NoteForm() {
const [isPending, startTransition] = useTransition();
const save = () => startTransition(async () => { await createNote('Idée'); });
return <button onClick={save} disabled={isPending}>Créer</button>;
}Cas d'usage : Mutations sans route API manuelle : formulaires, boutons d'action, appels RPC typés entre client et serveur.
Validation des données cote serveur en complément du client, car la validation client est contournable. Avec Server Actions, le même schema Zod peut valider des deux cotes.
Comme un double contrôle de sécurité : le portique (client) puis la fouille (serveur). Le portique seul ne suffit pas.
'use server';
const schema = z.object({ email: z.string().email() });
export async function createUser(formData: FormData) {
const result = schema.safeParse(Object.fromEntries(formData));
if (!result.success) return { errors: result.error.flatten() };
await db.users.create(result.data);
}Cas d'usage : Toute mutation de données : la validation client est UX, la validation serveur est sécurité.
Primitives reactives a grain fin qui mettent a jour le DOM directement sans re-rendu de composant. Popularises par Preact/SolidJS, en discussion pour React.
Comme un tableau d'affichage électronique : seule la case qui change se met a jour, pas tout le panneau.
// Avec @preact/signals-react
import { signal, computed } from '@preact/signals-react';
const count = signal(0);
const double = computed(() => count.value * 2);
function App() {
return <p>{count} x 2 = {double}</p>;
}Cas d'usage : Performance maximale avec des mises a jour granulaires sans réconciliation de tout le sous-arbre.
Pattern qui permet au parent de passer du contenu JSX dans des emplacements nommes du composant enfant, via des props ou children spécifiques.
Comme un formulaire administratif avec des cases a remplir : chaque case (slot) attend un contenu spécifique.
function Layout({ header, sidebar, children }) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
<Layout header={<Nav />} sidebar={<Menu />}>Contenu</Layout>Cas d'usage : Composants de layout configurables avec plusieurs zones de contenu (header, footer, sidebar).
Les pages HTML sont générées au moment du build, pas a la requête. Résultat ultra-rapide puisque le CDN sert des fichiers statiques pre-générés.
Comme imprimer des flyers a l'avance : pas besoin de les créer a la demande, ils sont déjà prêts a distribuer.
// Next.js - page statique par defaut dans App Router
export default async function Blog() {
const posts = await getPosts(); // execute au build
return posts.map(p => <article key={p.id}>{p.title}</article>);
}
// export const dynamic = 'force-static';Cas d'usage : Blogs, documentation, pages marketing dont le contenu ne change pas a chaque requête.
Le serveur génère le HTML complet a chaque requête. Le navigateur affiche la page immédiatement puis l'hydraté avec React pour la rendre interactive.
Comme un plat prépare au restaurant (serveur) vs un kit a assembler chez soi (CSR) : le client reçoit un plat déjà prêt.
// Next.js App Router
export default async function Page() {
const data = await fetch('https://api.example.com/data');
const posts = await data.json();
return <PostList posts={posts} />;
}Cas d'usage : Pages publiques nécessitant un bon SEO et un premier affichage rapide (e-commerce, blogs, landing pages).
Données internes et mutables d'un composant. Quand le state change, React re-rend le composant pour refléter la nouvelle valeur.
Comme le score dans un jeu vidéo : il change au fil du temps et l'affichage se met a jour automatiquement.
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Clics: {count}
</button>
);Cas d'usage : Stocker des données dynamiques propres a un composant (formulaires, toggles, compteurs).
Pattern qui permet au consommateur d'un composant de personnaliser les transitions d'état en fournissant son propre reducer, fusionnant avec le comportement par défaut.
Comme un pilote automatique avec override : l'avion vole seul mais le pilote peut reprendre le contrôle a tout moment.
function useToggle({ reducer = defaultReducer } = {}) {
const [state, dispatch] = useReducer(reducer, { on: false });
const toggle = () => dispatch({ type: 'TOGGLE' });
return { ...state, toggle };
}
// L'utilisateur peut injecter son reducer custom
const { on, toggle } = useToggle({
reducer: (state, action) => action.type === 'TOGGLE' && tooMany ? state : defaultReducer(state, action)
});Cas d'usage : Librairies/composants génériques ou l'utilisateur doit pouvoir overrider le comportement interne.
Outil de développement et documentation de composants UI en isolation. Chaque 'story' montre un état du composant avec ses variantes.
Comme un catalogue IKEA : chaque meuble (composant) est photographie dans tous ses coloris et configurations.
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
const meta: Meta<typeof Button> = { component: Button };
export default meta;
export const Primary: StoryObj = { args: { variant: 'primary', children: 'Click' } };
export const Disabled: StoryObj = { args: { disabled: true, children: 'Off' } };Cas d'usage : Développer, tester visuellement et documenter les composants UI d'un design system.
Le serveur envoie le HTML en flux continu au lieu d'attendre la page entière ; les parties lentes (sous Suspense) arrivent au fur et à mesure. Depuis React 19.2 (1er octobre 2025), React regroupe la révélation des frontières Suspense proches dans le temps (Suspense batching) pour éviter les affichages saccadés, et les API Web Streams (renderToReadableStream, prerender, résume) fonctionnent aussi sous Node.js.
Comme regarder un match en streaming : tu vois l'action en direct au lieu d'attendre que l'enregistrement soit complet.
// Next.js App Router avec Suspense
export default function Page() {
return (
<main>
<Header /> {/* envoye immediatement */}
<Suspense fallback={<Skeleton />}>
<SlowDataSection /> {/* stream quand pret */}
</Suspense>
</main>
);
}Cas d'usage : Pages avec des sections de vitesses différentes : le header s'affiche vite, les données arrivent en stream.
Composant de développement qui active des vérifications supplémentaires et double les rendus et effets pour révéler les effets de bord impurs. Depuis React 19 (5 décembre 2024), useMemo et useCallback réutilisent leur résultat du premier rendu lors du double rendu ; React 19.3 (9 septembre 2026) double aussi l'invocation des effets pendant l'hydratation.
Comme un professeur qui fait refaire l'exercice deux fois pour vérifier que le résultat est toujours le même.
<React.StrictMode>
<App />
</React.StrictMode>
// En dev, useEffect s'execute 2 fois pour detecter les bugsCas d'usage : Activer en développement pour détecter les effets de bord, les API dépréciées et les problèmes de cleanup.
React 19 (5 décembre 2024, guide de migration publié le 25 avril 2024) supprime les API dépréciées de longue date : propTypes et defaultProps sur les composants fonction (utiliser TypeScript et les paramètrès par défaut), les refs chaînés, le legacy Context, React.createFactory, ReactDOM.render, hydraté, unmountComponentAtNode, findDOMNode et react-test-renderer/shallow. Les builds UMD disparaissent aussi. Le codemod react/19/migration-recipe automatise l'essentiel.
Un grand déménagement : ce qui était étiqueté « à jeter » depuis des années part enfin à la benne.
// ❌ React 18
function Card({ title }) { /* ... */ }
Card.propTypes = { title: PropTypes.string };
Card.defaultProps = { title: 'Sans titre' };
ReactDOM.render(<App />, document.getElementById('root'));
// ✅ React 19
function Card({ title = 'Sans titre' }: { title?: string }) { /* ... */ }
createRoot(document.getElementById('root')).render(<App />);
// Migration automatisée
// npx codemod@latest react/19/migration-recipe
// npx types-react-codemod@latest preset-19 ./srcCas d'usage : Question d'entretien classique : « qu'est-ce qui casse en passant à React 19 ? » ; audit d'une base legacy avant montée de version.
Composant qui affiche un fallback pendant que ses enfants chargent des données ou du code asynchrone. Pilier du rendu concurrent.
Comme un écran 'Chargement...' au cinéma pendant que le projectionniste change de bobine.
const LazyPage = React.lazy(() => import('./Page'));
<Suspense fallback={<Spinner />}>
<LazyPage />
</Suspense>Cas d'usage : Afficher un loader pendant le chargement paresseux de composants ou de données avec React Query.
Librairie de fetching de Vercel basée sur la stratégie stale-while-revalidate : affiche les données en cache immédiatement, puis revalide en arrière-plan.
Comme un journal du matin : tu lis celui d'hier (cache) en attendant que le facteur apporte le nouveau (revalidation).
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then(r => r.json());
function Profile() {
const { data, error, isLoading } = useSWR('/api/user', fetcher);
if (isLoading) return <Spinner />;
return <p>{data.name}</p>;
}Cas d'usage : Alternative plus légère a TanStack Query pour des cas de fetching simples.
Librairie de gestion de l'état serveur (fetching, caching, synchronisation). Gère automatiquement le cache, la revalidation, le retry et les mises a jour optimistes.
Comme un concierge d'hôtel : il anticipe tes besoins, garde tes affaires en cache et les rafraîchit discrètement.
const { data, isLoading } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
});Cas d'usage : Toute application qui consomme une API REST/GraphQL et a besoin de cache intelligent et de revalidation.
Framework full-stack bâti sur TanStack Router (routage typé) qui ajoute SSR, streaming, server functions et server routes avec middleware composable, sur Vite ou Rsbuild. Toujours en Release Candidate au 27 septembre 2026 (paquet @tanstack/react-start 1.168.58 publié le 23 septembre 2026), déployable sur Netlify, Vercel, Cloudflare, Node. Les server functions se déclarent avec createServerFn().validator().handler() et se consomment via useServerFn ou les loaders.
Un kit de construction où chaque pièce est étiquetée et typée : impossible d'assembler un loader avec la mauvaise route.
import { createServerFn } from '@tanstack/react-start';
import { z } from 'zod';
export const createUser = createServerFn({ method: 'POST' })
.validator(z.object({ name: z.string().min(1) }))
.handler(async ({ data }) => {
return db.users.insert({ name: data.name }); // serveur uniquement
});
// Route : préchargement typé
export const Route = createFileRoute('/users')({
loader: () => getUsers(),
});
// Composant : const create = useServerFn(createUser);Cas d'usage : Équipes déjà sur TanStack Query/Router qui veulent un full-stack très typé sans adopter Next.js ni les RSC.
Bundler incrémental écrit en Rust, intégré à Next.js. Stable pour next dev depuis Next.js 15 et bundler par défaut pour dev et build depuis Next.js 16 (21 octobre 2025), avec 2 à 5× de gain annoncé au build et jusqu'à 10× sur le Fast Refresh. Next.js 16.3 (3 août 2026) active par défaut le cache disque en dev et en build (jusqu'à 90 % de mémoire en moins en dev) et ajoute import.meta.glob. Les plugins webpack ne sont pas supportés ; retour possible avec --webpack.
Un chef qui ne recuit que l'ingrédient modifié au lieu de refaire tout le plat à chaque retouche.
// package.json — Turbopack est le défaut, rien à configurer
{ "scripts": { "dev": "next dev", "build": "next build" } }
// Retour à webpack si un plugin l'exige : next build --webpack
// next.config.ts — options Turbopack
export default {
turbopack: {
resolveAlias: { underscore: 'lodash' },
resolveExtensions: ['.mdx', '.tsx', '.ts', '.jsx', '.js', '.json'],
},
};
// Vite-compatible : const posts = import.meta.glob('./posts/*.md', { eager: true });Cas d'usage : Tout projet Next.js 16+ ; gros monorepos où le temps de compilation et la mémoire du dev server étaient le goulot.
API stable depuis React 19 (5 décembre 2024) qui lit une Promise ou un Context directement pendant le rendu. Contrairement aux hooks classiques, use() peut être appelée dans une condition ou une boucle. Sur une Promise, le composant suspend jusqu'à la résolution (Suspense) ; sur un Context, elle remplace useContext.
Comme un lecteur universel : qu'on lui donne un CD (Promise) ou une clé USB (Context), il lit les deux.
// Server Component : on passe la Promise sans l'attendre
export default function Page() {
const commentsPromise = db.comments.list();
return (
<Suspense fallback={<Spinner />}>
<Comments promise={commentsPromise} />
</Suspense>
);
}
// Client Component
'use client';
function Comments({ promise }) {
const comments = use(promise); // suspend jusqu'à résolution
const theme = use(ThemeContext); // remplace useContext
return comments.map(c => <p key={c.id}>{c.text}</p>);
}Cas d'usage : Consommer côté client une Promise créée par un Server Component (streaming des données), ou lire un Context sous condition. En React 19.3 (9 septembre 2026), use(browser()) permet aussi de rendre un composant uniquement dans le navigateur.
Hook stable de React 19 (5 décembre 2024) qui remplace l'ancien useFormState. Il enveloppe une Action et retourne [state, dispatch, isPending] : l'Action reçoit l'état précédent et le payload (souvent un FormData), et son retour devient le nouvel état. Un troisième argument permalink permet la soumission avant hydratation (progressive enhancement).
Un formulaire papier qu'on renvoie avec, agrafé dessus, le dernier accusé de réception : on sait toujours où l'on en est.
'use client';
import { useActionState } from 'react';
import { updateName } from './actions'; // Server Function
function Form() {
const [state, submitAction, isPending] = useActionState(
async (prev, formData) => ({ error: await updateName(formData.get('name')) ?? null }),
{ error: null }
);
return <form action={submitAction}>
<input name="name" />
<button disabled={isPending}>Valider</button>
{state.error && <p>{state.error}</p>}
</form>;
}Cas d'usage : Formulaire avec erreurs de validation renvoyées par le serveur et bouton désactivé pendant l'envoi, sans état local manuel.
Hook qui memoize une fonction pour conserver la même référence entre les rendus. Équivalent a useMemo(() => fn, deps).
Comme donner toujours le même numéro de téléphone a un contact : il n'a pas besoin de mettre a jour son carnet a chaque fois.
const handleClick = useCallback((id) => {
setItems(prev => prev.filter(i => i.id !== id));
}, []); // reference stable
<MemoizedList onDelete={handleClick} />Cas d'usage : Stabiliser la référence d'un callback passé à un enfant mémoïsé. Avec React Compiler 1.0 (7 octobre 2025), le compilateur produit ces références stables lui-même ; useCallback manuel sert de soupape quand on veut garder la main.
Hook qui lit la valeur du Context le plus proche dans l'arbre. Depuis React 19 (5 décembre 2024), use(Context) fait la même chose mais peut être appelé sous condition ; useContext reste valide et n'est pas déprécié.
Comme la radio FM : tout le monde dans la zone capte le même signal sans câble direct.
const ThemeCtx = createContext('light');
function App() {
return (
<ThemeCtx value="dark"> {/* React 19 : plus de .Provider */}
<Toolbar />
</ThemeCtx>
);
}
function Button() {
const theme = useContext(ThemeCtx); // ou use(ThemeCtx)
return <button className={theme} />;
}Cas d'usage : Partager des données globales (theme, langue, user) sans passer par chaque niveau de props.
Hook qui retourne une version différée d'une valeur, permettant a React de prioriser les mises a jour urgentes et de différer le re-rendu du contenu non critique.
Comme un écho : ta voix (valeur originale) arrive d'abord, puis l'écho (valeur différée) suit avec un léger décalage.
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// L'input reste reactif, la liste se met a jour en differe
<input value={query} onChange={e => setQuery(e.target.value)} />
<HeavyList filter={deferredQuery} />Cas d'usage : Alternative a useTransition quand tu ne contrôles pas le setter de l'état (props venant du parent).
Hook pour synchroniser un composant avec un système externe (API, DOM, timer). S'exécute après le rendu, avec un tableau de dépendances pour contrôler quand il se relance.
Comme un abonnement a un journal : tu t'abonnes (setup), tu reçois les numéros (re-runs), et tu te desabonnes (cleanup) en partant.
useEffect(() => {
const ctrl = new AbortController();
fetch('/api/data', { signal: ctrl.signal })
.then(r => r.json())
.then(setData);
return () => ctrl.abort(); // cleanup
}, [userId]); // re-run si userId changeCas d'usage : Appels API, abonnements WebSocket, synchronisation avec le DOM ou des librairies externes.
Hook stable depuis React 19.2 (1er octobre 2025) qui extrait d'un effet la logique « événementielle » non réactive. La fonction retournée lit toujours les dernières props et états sans figurer dans le tableau de dépendances, ce qui évite de resynchroniser l'effet (reconnexion, redémarrage de timer) pour une valeur qui ne devrait pas le déclencher.
Un interphone : la connexion reste ouverte (l'effet), mais le message diffusé (l'événement) est toujours le plus récent.
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connecté !', theme); // lit le theme courant
});
useEffect(() => {
const conn = createConnection(roomId);
conn.on('connected', () => onConnected());
conn.connect();
return () => conn.disconnect();
}, [roomId]); // ✅ theme absent : changer de thème ne reconnecte pas
}Cas d'usage : Notifications, analytics ou logs déclenchés depuis un abonnement, un timer ou un WebSocket, dépendant de valeurs qui changent souvent.
Hook qui donne l'état de soumission du formulaire parent (pending, data, method, action). Doit être utilise dans un composant enfant du <form>.
Comme un indicateur lumineux sur une machine a café : il te dit si elle est en train de préparer (pending) ou prête.
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Envoi...' : 'Envoyer'}
</button>
);
}Cas d'usage : Désactiver le bouton submit et afficher un spinner pendant la soumission d'un formulaire avec Server Actions.
Hook qui génère un identifiant unique stable entre serveur et client, utile pour les attributs d'accessibilite comme htmlFor et aria-describedby.
Comme un tampon officiel : le même numéro est attribue que tu sois au guichet (serveur) ou en ligne (client).
function Input({ label }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}Cas d'usage : Générer des IDs pour les associations label/input sans risque de collision en SSR.
Hook qui personnalise la valeur exposée par la ref d'un composant, pour n'exposer que quelques méthodes (focus, reset) au parent. Depuis React 19 (5 décembre 2024), la ref arrive comme une prop ordinaire : forwardRef n'est plus nécessaire et sera déprécié.
Comme un interphone d'immeuble : tu choisis quels boutons exposer, pas toutes les clés de l'appartement.
// React 19 : `ref` est une prop, plus besoin de forwardRef
function FancyInput({ ref, ...props }) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; },
}), []);
return <input ref={inputRef} {...props} />;
}
// Parent
const ref = useRef(null);
<FancyInput ref={ref} />; ref.current.focus();Cas d'usage : Exposer une API impérative limitée a un composant enfant (focus, scroll, reset).
Hook qui s'exécute avant toute mutation DOM, conçu pour les librairies CSS-in-JS qui doivent injecter des balises <style> avant le layout.
Comme poser le papier peint avant d'installer les meubles : les styles sont prêts avant que le DOM n'existe.
// Reserve aux auteurs de libs CSS-in-JS
useInsertionEffect(() => {
const style = document.createElement('style');
style.textContent = '.btn { color: red; }';
document.head.appendChild(style);
return () => style.remove();
}, []);Cas d'usage : Exclusivement réserve aux librairies CSS-in-JS (styled-components, Émotion) pour injecter du CSS.
Identique a useEffect mais s'exécute de manière synchrone après les mutations DOM et avant que le navigateur ne peigne. Bloque le rendu visuel.
Comme repositionner les meubles avant de prendre la photo : tout est en place avant que l'utilisateur ne voie.
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipPos(height + 10);
}, []);Cas d'usage : Mesurer le DOM (position, taille) ou corriger un layout avant l'affichage pour éviter un flash visuel.
Hook qui memoize le résultat d'un calcul coûteux et ne le recalcule que si ses dépendances changent.
Comme mettre un plat au frigo : tu ne recuisines pas si les ingrédients n'ont pas change, tu ressers le même plat.
const sortedItems = useMemo(
() => items.sort((a, b) => a.price - b.price),
[items]
);
// Ne retrie que si 'items' changeCas d'usage : Calculs coûteux (tri, filtrage) recalculés à chaque rendu. Avec React Compiler 1.0 (7 octobre 2025), ces mémoïsations sont insérées automatiquement au build : useMemo manuel reste une porte de sortie, plus une obligation.
Hook stable depuis React 19 (5 décembre 2024) qui affiche un état optimiste pendant qu'une Action est en cours. Si l'Action échoue, React revient automatiquement à la valeur réelle ; si elle réussit, état optimiste et état réel convergent dans le même rendu, sans rendu supplémentaire.
Comme cocher 'lu' sur un message avant que le serveur confirme : si ca échoué, la coche disparaît.
const [optimisticLikes, addOptimistic] = useOptimistic(
likes,
(state, newLike) => [...state, newLike]
);
function handleLike() {
startTransition(async () => { // obligatoire : dans une Action
addOptimistic({ id: 'temp', user: 'moi' });
await saveLike(); // échec => retour à `likes`
});
}Cas d'usage : Likes, commentaires, toggles : feedback instantané a l'utilisateur pendant la requête serveur.
Hook alternatif a useState pour gérer un état complexe avec une logique de type action/reducer, inspire de Redux.
Comme un standard téléphonique : tu envoies un message (action) et l'opérateur (reducer) décidé quoi faire.
const reducer = (state, action) => {
switch(action.type) {
case 'increment': return { count: state.count + 1 };
case 'reset': return { count: 0 };
default: return state;
}
};
const [state, dispatch] = useReducer(reducer, { count: 0 });Cas d'usage : États complexes avec plusieurs sous-valeurs interdépendantes ou quand la logique de transition est élaborée.
Hook qui retourne un objet mutable {current} persistant entre les rendus, sans déclencher de re-rendu quand il change. Sert a référencer des éléments DOM ou stocker des valeurs.
Comme un tiroir secret dans un bureau : tu y ranges des choses sans que personne ne le remarque (pas de re-rendu).
const inputRef = useRef(null);
const timerRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
timerRef.current = setInterval(tick, 1000);
return () => clearInterval(timerRef.current);
}, []);Cas d'usage : Accéder au DOM (focus, scroll), stocker des IDs de timer, garder la valeur précédenté d'un state.
Hook qui ajoute un état local a un composant fonctionnel. Retourne la valeur courante et une fonction pour la mettre a jour.
Comme un post-it sur le frigo : tu lis la valeur et tu la remplaces quand elle change.
const [name, setName] = useState('');
// Mise a jour fonctionnelle (recommandee)
setCount(prev => prev + 1);
// Initialisation couteuse avec lazy init
const [data, setData] = useState(() => expensiveCalc());Cas d'usage : Tout état local simple : champs de formulaire, toggles, compteurs, données UI.
Hook pour s'abonner a un store externe (non-React) de manière compatible avec le rendu concurrent, évitant les tearing (desynchro visuelle).
Comme un écran d'aéroport synchronise : tous les passagers voient les mêmes horaires, pas de version périmée.
const width = useSyncExternalStore(
(cb) => { window.addEventListener('resize', cb); return () => window.removeEventListener('resize', cb); },
() => window.innerWidth,
() => 1024 // server snapshot
);Cas d'usage : Intégration avec des stores externes (Zustand, Redux interne) ou des API navigateur (resize, online).
Hook qui marque une mise à jour comme non urgente : React peut la différer pour garder l'interface réactive. Depuis React 19 (5 décembre 2024), startTransition accepte une fonction async : c'est une « Action », qui gère automatiquement l'état pending, les erreurs et les mises à jour optimistes.
Comme un panneau 'en cours de rénovation' : les clients voient l'ancien décor pendant que tu renoves en arrière-plan.
const [isPending, startTransition] = useTransition();
// React 18 : mise à jour lourde non urgente
startTransition(() => setFiltered(heavyFilter(query)));
// React 19 : Action asynchrone
function handleSave() {
startTransition(async () => {
const err = await updateName(name); // isPending = true pendant l'await
if (err) setError(err);
});
}Cas d'usage : Filtrage de longues listes, navigation entre onglets, et depuis React 19 toute mutation asynchrone (soumission, appel serveur) dont on veut l'état isPending sans state manuel.
State management base sur des proxies JavaScript. L'état est mutable directement et Valtio détecte les changements automatiquement via Proxy.
Comme un miroir magique : tu modifies l'objet original et le reflet (composant) se met a jour automatiquement.
const state = proxy({ count: 0, text: 'hello' });
function Counter() {
const snap = useSnapshot(state);
return (
<button onClick={() => { state.count++; }}>
{snap.count}
</button>
);
}Cas d'usage : Développeurs préférant la mutation directe d'objets, prototypage rapide, migration depuis MobX.
Représentation légère du DOM réel en mémoire. React compare l'ancien et le nouveau Virtual DOM pour ne modifier que ce qui a change.
Comme un brouillon avant d'envoyer un email : tu fais tes corrections sur le brouillon, puis tu envoies seulement la version finale.
// React cree un arbre virtuel
// Ancien: <p>Bonjour</p>
// Nouveau: <p>Salut</p>
// React ne change que le texte du <p>, pas tout le DOMCas d'usage : Mécanisme interne qui optimise les mises a jour du DOM sans intervention manuelle.
Technique de rendu qui n'affiche que les éléments visibles dans le viewport, même pour des listes de milliers d'items. Les éléments hors écran ne sont pas montes.
Comme un ascenseur panoramique : tu ne vois que les étages devant toi, pas les 100 étages en même temps.
import { useVirtualizer } from '@tanstack/react-virtual';
const virtualizer = useVirtualizer({
count: 10000,
getScrollElement: () => parentRef.current,
estimateSize: () => 35,
});
// Ne rend que ~20 items visibles au lieu de 10000Cas d'usage : Listes ou tableaux de plus de quelques centaines d'éléments (logs, contacts, produits).
Vite est l'outil de build recommandé par l'équipe React pour une SPA sans framework depuis la dépréciation de CRA (14 février 2025). Version majeure courante : Vite 8 (12 mars 2026), suivie de Vite 8.1 (23 juin 2026). Le plugin @vitejs/plugin-react fournit Fast Refresh et, via reactCompilerPreset() avec @rolldown/plugin-babel, l'activation de React Compiler. C'est aussi le socle de React Router en mode framework et de TanStack Start.
Une cuisine équipée prête à l'emploi : on branche et on cuisine, sans monter les meubles soi-même.
// vite.config.js
import { defineConfig } from 'vite';
import react, { reactCompilerPreset } from '@vitejs/plugin-react';
import babel from '@rolldown/plugin-babel';
export default defineConfig({
plugins: [
react(),
babel({ presets: [reactCompilerPreset()] }), // React Compiler
],
});
// npm create vite@latest mon-app -- --template react-tsCas d'usage : SPA React, back-office interne, prototypes, bibliothèque de composants (avec Storybook ou Vitest).
Librairie de debugging qui alerte dans la console quand un composant se re-rend inutilement (mêmes props mais nouvelle référence).
Comme un détecteur de fuite d'eau : il te montre exactement ou tu perds des performances.
// wdyr.js (importer en premier)
import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';
whyDidYouRender(React);
// Sur un composant specifique
MyComponent.whyDidYouRender = true;Cas d'usage : Debugger les re-rendus excessifs pendant le développement pour trouver les fuites de performance.
Intégration de Zod (validation de schémas) avec React Hook Form via @hookform/resolvers pour une validation typesafe partageable entre client et serveur.
Comme un contrat notarié pour un formulaire : les règles sont définies une fois et appliquées partout.
const schema = z.object({
email: z.string().email('Email invalide'),
age: z.number().min(18, 'Minimum 18 ans'),
});
type FormData = z.infer<typeof schema>;
const { register, handleSubmit } = useForm<FormData>({
resolver: zodResolver(schema),
});Cas d'usage : Formulaires avec validation complexe et types TypeScript inferes automatiquement depuis le schema.
Librairie de state management minimaliste sans boilerplate. Un store = une fonction, pas de Provider requis, compatible avec le rendu concurrent.
Comme un carnet de notes partage au bureau : tout le monde y accède directement, sans passer par un secrétaire (Provider).
const useStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
function Counter() {
const count = useStore((s) => s.count);
return <span>{count}</span>;
}Cas d'usage : État global simple a moyen sans la complexité de Redux, idéal pour les projets de toute taille.