React 19 marque la stabilisation des Server Components après 2 ans d'expérimentation, et introduit plusieurs API qui changent la façon d'écrire des applications React. C'est probablement la mise à jour la plus impactante depuis l'arrivée des Hooks en 2019. Voici notre retour d'expérience chez Krealabs après avoir migré une dizaine de projets clients de React 18 vers React 19, avec les pièges classiques, les bonnes pratiques, et les cas où on déconseille de migrer.
01Server Components : par où commencer
Tout composant créé dans le dossier app/ d'une app Next.js est par défaut un Server Component. Cela signifie qu'il s'exécute sur le serveur (Node.js ou Edge runtime), accède directement à la base de données ou aux API, et n'envoie que du HTML statique au navigateur. Aucun JavaScript de ce composant n'est descendu côté client. On bascule en Client Component uniquement quand on a besoin d'interactivité (useState, useEffect, onClick, hooks navigateur). La règle d'or : commencez TOUT en Server Component, puis marquez explicitement 'use client' sur les feuilles qui en ont besoin. C'est l'inverse du pattern React 18 où tout était client par défaut.
02Le pattern composant client/serveur en pratique
Un pattern qu'on utilise systématiquement chez Krealabs : un composant parent Server qui fetche les données, et passe les props (incluant les fonctions au format Server Actions) à un composant enfant Client qui gère l'interactivité. Ce pattern garde le bundle JS minimal tout en permettant des UI riches.
// Server Component (parent)
async function ProductPage({ id }) {
const product = await db.product.findUnique({ where: { id } })
return <ProductCard product={product} addToCart={addToCartAction} />
}
// Client Component (enfant)
'use client'
import { useState } from 'react'
export function ProductCard({ product, addToCart }) {
const [quantity, setQuantity] = useState(1)
return (
<form action={addToCart}>
<input name="qty" value={quantity} />
<button>Ajouter</button>
</form>
)
}03Le hook use() pour les promesses
React 19 introduit use(), qui permet d'attendre une promesse ou de lire un Context dans un composant. Combiné avec Suspense, cela simplifie drastiquement le data fetching côté client. Le composant qui appelle use() suspendra jusqu'à résolution de la promesse, déclenchant le fallback du <Suspense> parent. C'est plus simple que useEffect + useState + isLoading, et 100% compatible avec le streaming SSR de Next.js.
'use client'
import { use, Suspense } from 'react'
export function Post({ promise }) {
const post = use(promise) // attend la promesse
return <article>{post.title}</article>
}
// Usage parent (Server Component)
export default function Page() {
const postPromise = fetch('/api/post/1').then(r => r.json())
return (
<Suspense fallback={<Skeleton />}>
<Post promise={postPromise} />
</Suspense>
)
}04Actions et useActionState
Les Server Actions sont matures : fonctions serveur appelables directement depuis un formulaire client. Plus besoin d'API route REST pour un POST de formulaire simple. Le hook useActionState gère l'état (pending, error, result) de manière idiomatique, et useFormStatus permet d'accéder à l'état de submission depuis un sous-composant. Sur nos formulaires de contact, devis, signup, on utilise ce pattern systématiquement — il remplace toute la boilerplate axios/fetch + useState + try/catch qu'on avait dans les apps React 18.
'use client'
import { useActionState } from 'react'
export function ContactForm() {
const [state, formAction, pending] = useActionState(submitContact, null)
return (
<form action={formAction}>
<input name="email" />
<button disabled={pending}>{pending ? 'Envoi…' : 'Envoyer'}</button>
{state?.error && <p>{state.error}</p>}
</form>
)
}05useOptimistic : l'UI qui répond instantanément
useOptimistic est probablement le hook le plus sous-estimé de React 19. Il permet d'afficher un état "optimiste" en attendant la confirmation du serveur. L'utilisateur voit l'effet de son action immédiatement (like, comment, ajout panier), même si la requête prend 500ms à revenir. Si elle échoue, on rollback automatiquement. C'est ce qui donne aux apps modernes ce feeling de fluidité instantanée. Une fois qu'on a goûté, on ne peut plus s'en passer.
'use client'
import { useOptimistic } from 'react'
export function LikeButton({ likes, onLike }) {
const [optimisticLikes, addLike] = useOptimistic(likes, (s) => s + 1)
return (
<button onClick={async () => {
addLike() // UI mise à jour immédiatement
await onLike() // serveur, peut être lent
}}>
❤️ {optimisticLikes}
</button>
)
}06Pièges classiques à éviter
Premier piège : utiliser 'use client' trop large. Marquer un composant feuille comme client n'a pas d'impact, mais marquer le layout principal force tout l'arbre en client — gaspillage massif du bundle JS. Deuxième piège : appeler une fonction asynchrone côté client en oubliant Suspense — le bug est subtil et coûte cher en debug. Troisième piège : oublier que les Server Components ne peuvent pas être interactifs, et essayer d'y mettre un onClick (erreur de build claire heureusement). Quatrième piège : partager du state entre Server et Client Components — impossible par nature. Pour le state global, utilisez TanStack Query, Zustand côté client, ou la DB côté serveur.
07Quand utiliser Server vs Client Components
Heuristique simple : tout ce qui peut être Server Component DOIT l'être. Les Client Components sont réservés à ce qui nécessite : event handlers (onClick, onChange, onSubmit), hooks (useState, useEffect, useRef), API navigateur (localStorage, window, document), bibliothèques third-party qui en ont besoin (Framer Motion, Mapbox, certains UI kits). Pour tout le reste — affichage de données, accès à la DB, fetch externe, calculs serveur — Server Component. Cette discipline divise par 2 à 3 le bundle JS final.
08Migration React 18 → 19 : la méthode
Pour les projets Next.js qui sont déjà sur App Router, la migration vers React 19 est généralement transparente : Next.js 16 l'utilise par défaut. Pour un projet Vite ou CRA en React 18, la migration est plus complexe : il faut adopter ou React Server Components (via Vite RSC ou Remix), ou rester en mode classique. Notre conseil : ne pas migrer brutalement si votre app actuelle marche bien. Profitez d'une refonte majeure ou d'un nouveau projet pour adopter React 19. Sur les projets clients existants, on attend qu'un besoin métier justifie le coût de migration.
Les Server Components changent le paradigme React de fond en comble. Bien utilisés, ils réduisent le bundle JS de 40 à 60% sur un site type, et permettent un mental model plus simple (le serveur = serveur, le client = client, fini les hacks). Mal utilisés, ils créent une confusion entre données serveur et état client. Investissez du temps dans la formation de l'équipe avant la migration. Chez Krealabs, on a passé environ 2 semaines d'apprentissage en équipe avant de se sentir vraiment à l'aise. Le gain en productivité ensuite est réel. Voir aussi notre retour sur la migration Tailwind CSS 4.
Écrit par
Maxime Dubois
Co-fondateur · Krealabs
Découvrir l'équipe



