React a fêté ses dix ans cette année et n’a jamais été aussi pertinent. Les Server Components sont devenus une fonctionnalité stable, la guerre des meta-frameworks s’est organisée en une hiérarchie claire, et l’écosystème environnant a mûri en un ensemble d’outils qui résolvent réellement des problèmes au lieu d’en créer de nouveaux. Le React de 2024 ne ressemble en rien à l’ère des class components, et c’est une bonne chose.
Server Components : le plus grand tournant depuis les Hooks
Les React Server Components ont changé le modèle mental de ce que peut être un composant React. Un composant peut désormais s’exécuter exclusivement sur le serveur, récupérer des données directement, accéder au système de fichiers, et n’envoyer aucun JavaScript au client. La frontière entre serveur et client ne se situe plus au niveau de la page mais au niveau du composant.
async function PostPage({ params }: { params: { slug: string } }) {
const post = await db.post.findUnique({ where: { slug: params.slug } });
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
<LikeButton postId={post.id} />
</article>
);
}
Le composant PostPage n’atteint jamais le navigateur. Seul LikeButton, marqué avec "use client", envoie du JavaScript. Ce n’est pas une optimisation de performance qu’on ajoute après coup. C’est la stratégie de rendu par défaut, et elle élimine des catégories entières de problèmes : les états de chargement pour les données initiales, les requêtes en cascade, et le poids du bundle lié aux dépendances serveur.
Gestion d’état : moins c’est mieux
L’ère où l’on mettait tout dans un store global est révolue. Les primitives propres à React couvrent la plupart des besoins, et les cas restants sont gérés par des librairies légères qui font une seule chose bien.
Zustand s’est imposé comme le choix par défaut pour l’état global. Pas de boilerplate, pas de providers qui englobent toute l’application, pas de cérémonial d’actions et de reducers. Un store est un hook, et un hook suffit.
import { create } from 'zustand';
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
total: () => number;
}
const useCart = create<CartStore>((set, get) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
total: () => get().items.reduce((sum, item) => sum + item.price, 0),
}));
Pour l’état serveur, TanStack Query gère le cache, la revalidation, les mises à jour optimistes et le refetching en arrière-plan. La distinction compte : l’état serveur et l’état client ont des cycles de vie fondamentalement différents, et les mélanger dans le même store crée une complexité qu’aucune abstraction ne peut masquer.
import { useQuery } from '@tanstack/react-query';
function usePosts(category: string) {
return useQuery({
queryKey: ['posts', category],
queryFn: () => fetch(`/api/posts?cat=${category}`).then((r) => r.json()),
staleTime: 5 * 60 * 1000,
});
}
Formulaires : enfin un problème résolu
React Hook Form combiné à Zod a transformé la gestion des formulaires d’un point de douleur récurrent en un problème résolu. Les schémas de validation sont partagés entre client et serveur, l’inférence de types coule automatiquement, et les performances restent solides parce que React Hook Form utilise des inputs non contrôlés sous le capot.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
message: z.string().min(10).max(500),
});
function ContactForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email ? <span>{errors.email.message}</span> : null}
<textarea {...register('message')} />
<button type="submit">Envoyer</button>
</form>
);
}
Le schéma est la source de vérité unique. Les types du formulaire, les règles de validation et les messages d’erreur en dérivent tous. Pas de duplication, pas de décalage entre ce que l’UI accepte et ce que le serveur attend.
Meta-Frameworks : Next.js a posé le standard
Next.js 14 avec l’App Router a consolidé sa position comme la manière par défaut de construire des applications React. La combinaison des Server Components, des server actions, du routing basé sur les fichiers et des optimisations intégrées pour les images, les polices et les métadonnées rend difficile de justifier la construction d’une app React from scratch.
'use server';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/posts');
}
Les server actions réduisent l’écart entre frontend et backend à un simple appel de fonction. Pas de routes API à câbler, pas de fetch à maintenir, pas de sérialisation à gérer. Le compromis est le couplage, mais pour la plupart des applications ce couplage est une fonctionnalité.
TypeScript : plus une option
L’adoption de TypeScript dans l’écosystème React a franchi le point de bascule où ne pas l’utiliser demande une justification. Chaque librairie majeure fournit ses types, chaque meta-framework génère du TypeScript par défaut, et l’écart d’expérience développeur entre un codebase typé et non typé se creuse à chaque amélioration des IDE.
La combinaison de satisfies, des const assertions et des template literal types a rendu TypeScript suffisamment expressif pour modéliser des patterns React complexes sans se battre contre le système de types. Les composants génériques, les props en union discriminée et le context type-safe ne sont plus des techniques avancées mais des patterns du quotidien.
type ButtonProps =
| { variant: 'link'; href: string; onClick?: never }
| { variant: 'action'; onClick: () => void; href?: never };
function Button(props: ButtonProps) {
if (props.variant === 'link') {
return <a href={props.href}>Lien</a>;
}
return <button onClick={props.onClick}>Action</button>;
}
La stack qui fonctionne
L’écosystème React en 2024 ne consiste pas à choisir la librairie la plus tendance. Il s’agit d’un ensemble d’outils qui s’intègrent bien, scalent de manière prévisible et ont suffisamment d’élan communautaire pour survivre au prochain cycle de hype. Next.js pour le framework, Zustand pour l’état client, TanStack Query pour l’état serveur, React Hook Form avec Zod pour les formulaires, et TypeScript partout. Cette stack couvre la grande majorité des applications web sans recourir à quoi que ce soit d’exotique, et cette fiabilité vaut plus que la nouveauté.