Fait partie de notre série Performance & Scalability
Lire le guide completComposants du serveur React 19 : ce qui a changé et pourquoi
React 19 est la version la plus importante depuis les hooks. Les composants serveur, qui ont commencé comme fonctionnalité expérimentale dans React 18, sont désormais stables et entièrement intégrés au modèle de rendu simultané. Mais les changements vont bien au-delà de la simple stabilisation du RSC : React 19 introduit des actions, un nouveau hook use(), l'intégration de formulaires, des mises à jour optimistes et une gestion des métadonnées de documents qui changent collectivement votre façon de penser le flux de données dans les applications React.
Ce guide se concentre sur ce qui a réellement changé entre React 18 et 19, explique le raisonnement architectural derrière chaque changement et montre les modèles de production qui remplacent les anciennes approches.
Points clés à retenir
- Les composants serveur s'exécutent uniquement sur le serveur : ils n'ont ni cycle de vie, ni état, ni API de navigateur.
- Le hook
use()remplaceawaitdans les composants clients pour consommer les promesses et le contexte- Les actions React 19 remplacent les modèles manuels
useState+fetchpour les soumissions de formulairesuseOptimisticpermet des mises à jour instantanées de l'interface utilisateur avant la confirmation du serveuruseFormStatusvous donne un état en attente sans percer les composants du formulaire- Les API de préchargement des actifs (
preload,preinit) vous permettent de contrôler le chargement des ressources à partir des composants- Les balises
<title>,<meta>et<link>dans les composants sont automatiquement hissées vers<head>- Les composants serveur et les composants clients forment une arborescence — Les composants clients ne peuvent pas importer de composants serveur
Quels sont réellement les composants du serveur
Avant de discuter de ce qui a changé, clarifiez ce que sont les composants serveur : des composants React qui s'affichent exclusivement sur le serveur et produisent une charge utile HTML + RSC que le client hydrate. Ils ne sont pas les mêmes que les composants clients rendus côté serveur.
Les principales différences :
| Composants du serveur | Composants clients | |
|---|---|---|
| Fonctionne sur | Serveur uniquement | Serveur (rendu initial) + Client |
| Peut utiliser des crochets | Non | Oui |
| Peut utiliser les API du navigateur | Non | Oui |
| Peut accéder à la base de données | Oui (directement) | Non (via API) |
| Impact sur la taille du paquet | Zéro | Oui |
| Peut être asynchrone | Oui | Non (sans Suspense) |
// Server Component — async, no hooks, direct DB access
async function UserProfile({ userId }: { userId: string }) {
// Direct database query — no API needed
const user = await db.query.users.findFirst({
where: eq(users.id, userId),
});
return (
<div>
<h1>{user.name}</h1>
{/* Client Component gets data as props */}
<UserActions userId={user.id} role={user.role} />
</div>
);
}
// Client Component — can use hooks, handles interactions
'use client';
function UserActions({ userId, role }: { userId: string; role: string }) {
const [isEditing, setIsEditing] = useState(false);
return (
<div>
<button onClick={() => setIsEditing(true)}>Edit</button>
{isEditing && <EditUserForm userId={userId} />}
</div>
);
}
Réagissez 19 actions
La plus grande amélioration ergonomique de React 19 concerne les actions, un moyen standardisé de gérer les opérations asynchrones déclenchées par les interactions des utilisateurs, en particulier les soumissions de formulaires.
Avant React 19, la gestion des formulaires était un passe-partout répétitif :
// React 18 — manual state management
function ContactForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setSubmitting(true);
setError(null);
try {
await createContact({ name, email });
} catch (err) {
setError(err.message);
} finally {
setSubmitting(false);
}
}
return (
<form onSubmit={handleSubmit}>
{/* ... */}
</form>
);
}
Les actions React 19 simplifient considérablement cela :
// React 19 — useActionState
'use client';
import { useActionState } from 'react';
async function createContactAction(
prevState: { error?: string },
formData: FormData
) {
'use server'; // Server Action — runs on the server
const name = formData.get('name') as string;
const email = formData.get('email') as string;
try {
await createContact({ name, email });
return { success: true };
} catch (err) {
return { error: err.message };
}
}
function ContactForm() {
const [state, formAction, isPending] = useActionState(
createContactAction,
{}
);
return (
<form action={formAction}>
<input name="name" required />
<input name="email" type="email" required />
{state.error && <p className="text-red-500">{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
Le hook useActionState gère l'état en attente, les données de formulaire et la gestion des erreurs en un seul endroit. La directive 'use server' sur la fonction d'action la marque comme une action serveur : elle s'exécute sur le serveur, peut accéder directement à la base de données et le client ne voit jamais l'implémentation.
## Actions du serveur
Les actions du serveur sont des fonctions asynchrones avec la directive 'use server' qui s'exécutent sur le serveur lorsqu'elles sont appelées depuis le client. Ils remplacent les routes API pour la plupart des cas d'utilisation de mutation de données :
// app/actions/contacts.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { db } from '@ecosire/db';
import { contacts } from '@ecosire/db/schema';
export async function createContact(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
if (!name || !email) {
throw new Error('Name and email are required');
}
await db.insert(contacts).values({
name,
email,
organizationId: await getOrganizationId(), // From session
});
revalidatePath('/dashboard/contacts'); // Invalidate cached page
redirect('/dashboard/contacts'); // Redirect after success
}
Les actions du serveur peuvent être utilisées directement dans les éléments form :
import { createContact } from '@/app/actions/contacts';
export default function NewContactPage() {
return (
<form action={createContact}>
<input name="name" placeholder="Name" required />
<input name="email" placeholder="Email" type="email" required />
<button type="submit">Create Contact</button>
</form>
);
}
Pas de gestionnaire onSubmit, pas de preventDefault(), pas de manuel fetch() — le formulaire fonctionne tout simplement.
Le crochet use()
React 19 introduit le hook use() pour consommer des ressources (promesses et contexte) dans le rendu. Contrairement à await, il fonctionne avec le système Suspense de React :
// Before React 19 — had to resolve at the top level
export default async function Page() {
const posts = await getPosts(); // Blocks entire page
return <PostList posts={posts} />;
}
// React 19 — pass a promise, resolve with use()
export default function Page() {
const postsPromise = getPosts(); // Start fetch immediately
return (
<Suspense fallback={<PostsSkeleton />}>
<PostList postsPromise={postsPromise} />
</Suspense>
);
}
// Client Component using use() to consume the promise
'use client';
function PostList({ postsPromise }: { postsPromise: Promise<Post[]> }) {
const posts = use(postsPromise); // Suspends until resolved
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
Le hook use() remplace également useContext() — il peut consommer le contexte de manière conditionnelle (contrairement aux hooks, use() peut être appelé à l'intérieur de conditions et de boucles) :
'use client';
import { use } from 'react';
import { ThemeContext } from '@/contexts/theme';
function Button({ children }: { children: React.ReactNode }) {
const theme = use(ThemeContext); // Can be inside conditions
return (
<button className={theme === 'dark' ? 'bg-gray-800' : 'bg-white'}>
{children}
</button>
);
}
Mises à jour optimistes avec useOptimistic
useOptimistic fournit un retour instantané sur l'interface utilisateur avant la fin d'une opération du serveur, annulant automatiquement si l'opération échoue :
'use client';
import { useOptimistic, useTransition } from 'react';
interface Like {
id: string;
count: number;
userLiked: boolean;
}
function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: Like }) {
const [likes, setOptimisticLikes] = useOptimistic(
initialLikes,
(state, action: 'like' | 'unlike') => ({
...state,
count: action === 'like' ? state.count + 1 : state.count - 1,
userLiked: action === 'like',
})
);
const [isPending, startTransition] = useTransition();
async function toggleLike() {
const action = likes.userLiked ? 'unlike' : 'like';
startTransition(async () => {
setOptimisticLikes(action); // Instant UI update
// Server operation — if this fails, optimistic state reverts
await togglePostLike(postId, action);
});
}
return (
<button onClick={toggleLike} disabled={isPending}>
{likes.userLiked ? '♥' : '♡'} {likes.count}
</button>
);
}
La mise à jour optimiste s'affiche immédiatement, puis est validée (succès du serveur) ou annulée (échec du serveur). Les utilisateurs reçoivent un retour instantané sans attendre les allers-retours du réseau.
Documenter les métadonnées des composants
React 19 permet d'afficher les balises <title>, <meta> et <link> à l'intérieur de n'importe quel composant — elles sont automatiquement hissées vers le document <head> :
// Server Component — metadata hoists to <head> automatically
async function BlogPost({ slug }: { slug: string }) {
const post = await getPost(slug);
return (
<article>
<title>{post.title}</title>
<meta name="description" content={post.description} />
<link rel="canonical" href={`https://ecosire.com/blog/${slug}`} />
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
Cela fonctionne à la fois dans les composants serveur et client. Dans des frameworks comme Next.js, vous utiliserez toujours generateMetadata() pour un contrôle total sur les balises OpenGraph et les attributs hreflang – la prise en charge native de React 19 est plus basique. Mais pour les cas simples, cela élimine le besoin de next/head ou de bibliothèques similaires.
API de chargement d'actifs
React 19 fournit des API explicites pour le préchargement des ressources, vous donnant un contrôle précis sur la cascade de chargement des ressources :
import { preload, preinit, prefetchDNS, preconnect } from 'react-dom';
function BelowFoldSection() {
// When this component renders, preload the hero image for next section
preload('/images/hero-next.webp', { as: 'image' });
// Preinit a script (loads and executes immediately)
preinit('https://cdn.example.com/analytics.js', { as: 'script' });
// DNS prefetch for external resources
prefetchDNS('https://fonts.googleapis.com');
// Establish connection early
preconnect('https://api.ecosire.com');
return <section>{/* content */}</section>;
}
Ces API fonctionnent correctement avec le modèle de rendu de React : elles regroupent les indicateurs de ressources et les émettent au point optimal du document, même au plus profond de l'arborescence des composants.
Pièges courants et solutions
Piège 1 : Essayer d'utiliser des hooks dans les composants serveur
Les composants serveur ne peuvent pas utiliser useState, useEffect, useContext ou tout autre hook. Si vous en avez besoin, ajoutez 'use client' au composant. L'erreur est généralement : Error: Hooks can only be called inside a function component.
Piège 2 : Importer des composants serveur à partir de composants clients
Les composants clients ne peuvent pas importer de composants serveur (l'inverse est très bien). En effet, les composants clients s'exécutent dans le navigateur où les composants serveur n'existent pas. Si vous devez les composer, transmettez les composants du serveur en tant qu'accessoires children :
// Wrong — Client Component cannot import Server Component
'use client';
import { ServerUserProfile } from './server-user-profile'; // Error
// Correct — pass as children from a Server Component parent
// Parent (Server):
<ClientShell>
<ServerUserProfile userId={userId} />
</ClientShell>
// ClientShell:
'use client';
function ClientShell({ children }: { children: React.ReactNode }) {
return <div className="shell">{children}</div>;
}
Piège 3 : erreurs de sérialisation lors du passage des accessoires du serveur au client
Seules les données sérialisables JSON peuvent franchir la frontière serveur-client en tant qu'accessoires. Les fonctions, instances de classe, Map, Set et Dates (en tant qu'objets) ne peuvent pas être sérialisées. Convertir les dates en chaînes ISO ; remplacez les fonctions par des identifiants de chaîne.
Piège 4 : les actions du serveur ne valident pas l'entrée
Ne faites jamais confiance aux données fournies par le client dans les actions du serveur. Validez toujours avec Zod ou similaire avant d'écrire dans la base de données :
'use server';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2).max(255),
email: z.string().email(),
});
export async function createContact(formData: FormData) {
const result = schema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});
if (!result.success) {
return { error: result.error.flatten().fieldErrors };
}
await db.insert(contacts).values(result.data);
}
Questions fréquemment posées
Les composants serveur sont-ils disponibles en dehors de Next.js ?
Les composants du serveur React sont une fonctionnalité de React, mais ils nécessitent un cadre pour gérer l'infrastructure de rendu du serveur : routage, regroupement, streaming. Next.js App Router est l’implémentation la plus mature. Les configurations basées sur Remix, Astro et Vite ajoutent la prise en charge RSC. L’utilisation de RSC sans framework nécessite un travail d’infrastructure personnalisé important.
Comment les actions du serveur se comparent-elles aux routes de l'API REST ?
Les actions du serveur sont plus simples pour les mutations colocalisées avec l'interface utilisateur : pas d'URL de point de terminaison à gérer, pas d'appel fetch() à écrire, pas de passe-partout de gestion des erreurs. Les routes d'API REST sont meilleures pour les opérations appelées depuis l'extérieur de l'application Web (applications mobiles, webhooks, intégrations tierces), pour les API publiques qui nécessitent une documentation et lorsque vous avez besoin de codes d'état HTTP explicites. Utilisez les deux dans la même application en fonction du cas d’utilisation.
Quel est l'impact sur les performances des composants serveur ?
Les composants serveur réduisent la taille du bundle JavaScript (le code du composant n'est jamais envoyé au navigateur), éliminent les cascades de récupération de données côté client et permettent la diffusion de flux HTML via Suspense. Le compromis est le coût de calcul du serveur : le rendu s'effectue sur vos serveurs, et non sur l'appareil du client. Pour les pages contenant beaucoup de données, il s'agit presque toujours d'un gain net.
Puis-je mélanger React 18 et React 19 dans une base de code ?
Tout le code React s'exécute comme React 19 — il n'y a pas de versionnage par fichier. La question est de savoir si votre code React 18 existant fonctionne dans React 19. La plupart du code React 18 fonctionne inchangé. Les principaux changements majeurs concernent les références (maintenant un accessoire régulier), la suppression de ReactDOM.render et quelques modifications de frappe dans @types/react. Exécutez les codemods React 19 pour une migration automatisée.
Comment tester les composants du serveur ?
Les composants du serveur peuvent être testés avec React Testing Library en utilisant async render. Pour les actions du serveur, testez-les en tant que fonctions asynchrones simples avec des appels de base de données simulés. Les tests de bout en bout avec Playwright couvrent l'intégration complète du composant serveur + du composant client sans aucune configuration particulière : ils testent la sortie HTML finale.
Prochaines étapes
Les composants serveur React 19 représentent un changement fondamental dans la façon dont les applications Web modernes sont créées. L'équipe frontend d'ECOSIRE a construit des applications de production sur cette architecture : 249 pages avec des composants serveur, des actions serveur et des mises à jour optimistes alimentant les flux de travail des utilisateurs réels.
Si vous avez besoin d'aide pour migrer de React 18 vers 19, pour concevoir une nouvelle application RSC-first, ou si vous souhaitez simplement intégrer une ingénierie frontend experte dans votre équipe, explorez nos services de développement.
Rédigé par
ECOSIRE Research and Development Team
Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.
Articles connexes
k6 Load Testing: Stress-Test Your APIs Before Launch
Master k6 load testing for Node.js APIs. Covers virtual user ramp-ups, thresholds, scenarios, HTTP/2, WebSocket testing, Grafana dashboards, and CI integration patterns.
Next.js 16 App Router: Production Patterns and Pitfalls
Production-ready Next.js 16 App Router patterns: server components, caching strategies, metadata API, error boundaries, and performance pitfalls to avoid.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Plus de Performance & Scalability
k6 Load Testing: Stress-Test Your APIs Before Launch
Master k6 load testing for Node.js APIs. Covers virtual user ramp-ups, thresholds, scenarios, HTTP/2, WebSocket testing, Grafana dashboards, and CI integration patterns.
Nginx Production Configuration: SSL, Caching, and Security
Nginx production configuration guide: SSL termination, HTTP/2, caching headers, security headers, rate limiting, reverse proxy setup, and Cloudflare integration patterns.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Odoo vs Acumatica: Cloud ERP for Growing Businesses
Odoo vs Acumatica compared for 2026: unique pricing models, scalability, manufacturing depth, and which cloud ERP fits your growth trajectory.
Testing and Monitoring AI Agents in Production
A complete guide to testing and monitoring AI agents in production environments. Covers evaluation frameworks, observability, drift detection, and incident response for OpenClaw deployments.
Compliance Monitoring Agents with OpenClaw
Deploy OpenClaw AI agents for continuous compliance monitoring. Automate regulatory checks, policy enforcement, audit trail generation, and compliance reporting.