Aller au contenu principal
Retour au blog
Web

React 19 et les Server Components en pratique

React 19 stabilise les Server Components, introduit use() pour les promesses, useOptimistic pour l'UI optimiste, et améliore les Actions. Guide complet pour les utiliser correctement sur un vrai projet, avec retours terrain.

14 min1 026 mots

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.

En résumé

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.

React
Server Components
use()
Actions
useOptimistic
Web
Maxime Dubois

Écrit par

Maxime Dubois

Co-fondateur · Krealabs

Découvrir l'équipe

À propos de cet article

Rédigé par

Maxime Dubois

Co-fondateur · Krealabs

Méthodologie

Rédigé à partir de notre travail d'agence et de la documentation officielle des outils cités. Pas d'IA générative pour le fond éditorial.

Publié le
Parlons projet

Un sujet à creuser ensemble ?

Si cet article t'a parlé et que tu as un projet en cours (ou naissant), écris-nous — premier échange offert.