RAICODE
ProcessusProjetsBlogOffresClientsContact
development

React 19 : Guide Complet des Nouvelles Fonctionnalités

Découvrez les nouveautés majeures de React 19 : Actions, use(), Server Components, et plus. Guide pratique avec exemples de code.

Mustapha Hamadi
Développeur Full-Stack
5 décembre 2025
11 min read
React 19 : Guide Complet des Nouvelles Fonctionnalités
#React#JavaScript#Frontend
Partager :

title: "React 19 : Guide Complet des Nouvelles Fonctionnalités" description: "Découvrez les nouveautés majeures de React 19 : Actions, use(), Server Components, et plus. Guide pratique avec exemples de code." date: "2025-12-05" author: name: "Mustapha Hamadi" role: "Développeur Full-Stack" image: "/avatar.jpg" tags: ["React", "JavaScript", "Frontend"] category: "development" image: "/blog/react-19-nouvelles-fonctionnalites-guide-complet-hero.png" ogImage: "/blog/react-19-nouvelles-fonctionnalites-guide-complet-hero.png" featured: false published: true keywords: ["React 19", "React nouveautés", "Actions React", "use hook", "Server Components", "React Compiler", "useFormStatus", "useOptimistic", "développement frontend", "JavaScript framework", "React hooks", "mise à jour React"]

React 19 : Guide Complet des Nouvelles Fonctionnalités

React 19 marque un tournant majeur dans l'évolution du framework le plus populaire pour le développement frontend. Cette version introduit des fonctionnalités révolutionnaires qui simplifient drastiquement la gestion des formulaires, les requêtes asynchrones et l'expérience utilisateur. Dans ce guide complet, nous explorons chaque nouveauté avec des exemples pratiques que vous pouvez implémenter immédiatement dans vos projets.

Les Actions : Révolution dans la Gestion des Formulaires

Qu'est-ce qu'une Action ?

Les Actions sont la nouveauté phare de React 19. Elles transforment radicalement la façon dont nous gérons les soumissions de formulaires et les mutations de données. Fini les useState multiples pour gérer les états de chargement, d'erreur et de succès.

// Avant React 19 : Gestion manuelle complexe
function ContactFormOld() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [isPending, setIsPending] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState(false);

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    setIsPending(true);
    setError(null);

    try {
      await submitContact({ name, email });
      setSuccess(true);
    } catch (err) {
      setError('Une erreur est survenue');
    } finally {
      setIsPending(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... beaucoup de code */}
    </form>
  );
}
// React 19 : Simplicité avec les Actions
function ContactForm() {
  async function submitAction(formData: FormData) {
    'use server';

    const name = formData.get('name');
    const email = formData.get('email');

    await submitContact({ name, email });
  }

  return (
    <form action={submitAction}>
      <input name="name" placeholder="Votre nom" required />
      <input name="email" type="email" placeholder="Votre email" required />
      <SubmitButton />
    </form>
  );
}

useFormStatus : État du Formulaire Sans Props Drilling

Le hook useFormStatus permet aux composants enfants d'accéder à l'état du formulaire parent sans avoir à faire remonter les props.

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending, data, method, action } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? (
        <>
          <Spinner /> Envoi en cours...
        </>
      ) : (
        'Envoyer'
      )}
    </button>
  );
}

Avantages clés :

  • Pas besoin de props pour transmettre l'état de chargement
  • Fonctionne automatiquement avec le formulaire parent
  • Réutilisable dans n'importe quel formulaire

useActionState : Gestion Complète de l'État

Pour une gestion plus fine incluant les erreurs et les résultats, useActionState offre une solution complète.

import { useActionState } from 'react';

interface FormState {
  success: boolean;
  message: string;
  errors?: Record<string, string>;
}

function NewsletterForm() {
  const [state, formAction, isPending] = useActionState(
    async (prevState: FormState, formData: FormData): Promise<FormState> => {
      const email = formData.get('email') as string;

      // Validation
      if (!email.includes('@')) {
        return {
          success: false,
          message: 'Email invalide',
          errors: { email: 'Veuillez entrer un email valide' }
        };
      }

      // Soumission
      await subscribeToNewsletter(email);

      return {
        success: true,
        message: 'Inscription réussie !'
      };
    },
    { success: false, message: '' }
  );

  return (
    <form action={formAction}>
      <input
        name="email"
        type="email"
        placeholder="[email protected]"
        aria-invalid={!!state.errors?.email}
      />
      {state.errors?.email && (
        <span className="error">{state.errors.email}</span>
      )}

      <button type="submit" disabled={isPending}>
        {isPending ? 'Inscription...' : "S'inscrire"}
      </button>

      {state.success && (
        <p className="success">{state.message}</p>
      )}
    </form>
  );
}

Le Hook use() : Promesses et Contextes Simplifiés

Lire des Promesses Directement

Le nouveau hook use() permet de lire des promesses directement dans le rendu, en travaillant avec Suspense pour gérer les états de chargement.

import { use, Suspense } from 'react';

// Fonction qui retourne une promesse
async function fetchUserData(userId: string) {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
}

function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  // use() suspend le rendu jusqu'à la résolution
  const user = use(userPromise);

  return (
    <div className="profile">
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
    </div>
  );
}

// Utilisation avec Suspense
function App() {
  const userPromise = fetchUserData('123');

  return (
    <Suspense fallback={<ProfileSkeleton />}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

Lecture Conditionnelle du Contexte

Contrairement à useContext, use() peut être appelé conditionnellement.

import { use, createContext } from 'react';

const ThemeContext = createContext<'light' | 'dark'>('light');
const FeatureFlagContext = createContext({ darkMode: false });

function ThemedButton({ children }: { children: React.ReactNode }) {
  const features = use(FeatureFlagContext);

  // Lecture conditionnelle - impossible avec useContext
  const theme = features.darkMode ? use(ThemeContext) : 'light';

  return (
    <button className={`btn-${theme}`}>
      {children}
    </button>
  );
}

useOptimistic : Mises à Jour Instantanées

L'Interface Utilisateur Optimiste

useOptimistic permet de mettre à jour l'interface immédiatement, avant même que le serveur réponde. C'est essentiel pour une expérience utilisateur fluide.

import { useOptimistic, useTransition } from 'react';

interface Comment {
  id: string;
  text: string;
  author: string;
  pending?: boolean;
}

function CommentSection({ initialComments }: { initialComments: Comment[] }) {
  const [comments, setComments] = useState(initialComments);
  const [isPending, startTransition] = useTransition();

  const [optimisticComments, addOptimisticComment] = useOptimistic(
    comments,
    (state, newComment: Comment) => [...state, { ...newComment, pending: true }]
  );

  async function handleAddComment(formData: FormData) {
    const text = formData.get('text') as string;

    const newComment: Comment = {
      id: crypto.randomUUID(),
      text,
      author: 'Vous'
    };

    // Mise à jour optimiste immédiate
    addOptimisticComment(newComment);

    // Soumission au serveur
    startTransition(async () => {
      const savedComment = await saveComment(newComment);
      setComments(prev => [...prev, savedComment]);
    });
  }

  return (
    <div className="comments">
      <ul>
        {optimisticComments.map(comment => (
          <li
            key={comment.id}
            style={{ opacity: comment.pending ? 0.7 : 1 }}
          >
            <strong>{comment.author}</strong>: {comment.text}
            {comment.pending && <span> (envoi...)</span>}
          </li>
        ))}
      </ul>

      <form action={handleAddComment}>
        <input name="text" placeholder="Votre commentaire..." required />
        <button type="submit">Commenter</button>
      </form>
    </div>
  );
}

Exemple Pratique : Like/Unlike

function LikeButton({ postId, initialLiked }: { postId: string; initialLiked: boolean }) {
  const [liked, setLiked] = useState(initialLiked);
  const [optimisticLiked, toggleOptimisticLike] = useOptimistic(
    liked,
    (_, newValue: boolean) => newValue
  );

  async function handleLike() {
    const newValue = !optimisticLiked;
    toggleOptimisticLike(newValue);

    try {
      await updateLikeStatus(postId, newValue);
      setLiked(newValue);
    } catch (error) {
      // En cas d'erreur, l'état optimiste sera automatiquement réinitialisé
      console.error('Erreur lors du like:', error);
    }
  }

  return (
    <button onClick={handleLike} className={optimisticLiked ? 'liked' : ''}>
      {optimisticLiked ? '❤️' : '🤍'}
    </button>
  );
}

Server Components : Le Rendu Serveur Nouvelle Génération

Comprendre les Server Components

Les React Server Components (RSC) permettent de rendre des composants directement sur le serveur, réduisant drastiquement le JavaScript envoyé au client.

// Ce composant s'exécute UNIQUEMENT sur le serveur
// Pas de "use client" = Server Component par défaut

async function ProductPage({ productId }: { productId: string }) {
  // Accès direct à la base de données - pas d'API nécessaire
  const product = await db.products.findUnique({
    where: { id: productId },
    include: { reviews: true, category: true }
  });

  if (!product) {
    notFound();
  }

  return (
    <article className="product">
      <ProductImages images={product.images} />
      <h1>{product.name}</h1>
      <p className="price">{formatPrice(product.price)}</p>
      <p className="description">{product.description}</p>

      {/* Composant client pour l'interactivité */}
      <AddToCartButton productId={product.id} />

      <ReviewsList reviews={product.reviews} />
    </article>
  );
}

Quand Utiliser Client vs Server Components

// ✅ Server Component : Pas d'interactivité, données statiques
async function ArticleContent({ slug }: { slug: string }) {
  const article = await getArticle(slug);
  return <div dangerouslySetInnerHTML={{ __html: article.content }} />;
}

// ✅ Client Component : Interactivité requise
'use client';

import { useState } from 'react';

function ShareButton({ url }: { url: string }) {
  const [copied, setCopied] = useState(false);

  const handleShare = async () => {
    await navigator.clipboard.writeText(url);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <button onClick={handleShare}>
      {copied ? '✓ Copié !' : 'Partager'}
    </button>
  );
}

Règles de base :

  • Server Component : Données, contenu statique, accès base de données
  • Client Component : useState, useEffect, événements, browser APIs

React Compiler : Optimisation Automatique

Fini useMemo et useCallback

Le React Compiler (anciennement React Forget) analyse votre code et ajoute automatiquement les mémoïsations nécessaires.

// Avant : Mémoïsation manuelle
function ProductList({ products, filter }: Props) {
  const filteredProducts = useMemo(
    () => products.filter(p => p.category === filter),
    [products, filter]
  );

  const handleClick = useCallback(
    (id: string) => selectProduct(id),
    []
  );

  return (
    <ul>
      {filteredProducts.map(product => (
        <ProductItem
          key={product.id}
          product={product}
          onClick={handleClick}
        />
      ))}
    </ul>
  );
}

// Après avec React Compiler : Code naturel
function ProductList({ products, filter }: Props) {
  // Le compilateur détecte et optimise automatiquement
  const filteredProducts = products.filter(p => p.category === filter);

  const handleClick = (id: string) => selectProduct(id);

  return (
    <ul>
      {filteredProducts.map(product => (
        <ProductItem
          key={product.id}
          product={product}
          onClick={handleClick}
        />
      ))}
    </ul>
  );
}

Activation du Compilateur

// next.config.js (Next.js 15+)
module.exports = {
  experimental: {
    reactCompiler: true,
  },
};

Améliorations des Refs et du Contexte

Refs en Props Directes

Plus besoin de forwardRef - les refs peuvent être passées comme props normales.

// Avant React 19
const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
  return <input ref={ref} {...props} />;
});

// React 19 : Ref comme prop directe
function Input({ ref, ...props }: InputProps & { ref?: Ref<HTMLInputElement> }) {
  return <input ref={ref} {...props} />;
}

// Utilisation identique
function Form() {
  const inputRef = useRef<HTMLInputElement>(null);

  return <Input ref={inputRef} placeholder="Votre nom" />;
}

Fonctions de Nettoyage pour Refs

function VideoPlayer({ src }: { src: string }) {
  const videoRef = useRef<HTMLVideoElement>(null);

  return (
    <video
      ref={(element) => {
        if (element) {
          element.play();
        }
        // Fonction de nettoyage - nouveau en React 19
        return () => {
          element?.pause();
        };
      }}
      src={src}
    />
  );
}

Métadonnées Document Natives

Gestion Simplifiée du Head

React 19 gère nativement les balises <title>, <meta>, et <link> dans vos composants.

function BlogPost({ post }: { post: Post }) {
  return (
    <article>
      {/* Ces balises remontent automatiquement dans <head> */}
      <title>{post.title} | Mon Blog</title>
      <meta name="description" content={post.excerpt} />
      <meta property="og:title" content={post.title} />
      <meta property="og:image" content={post.coverImage} />
      <link rel="canonical" href={`https://monsite.com/blog/${post.slug}`} />

      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

Préchargement de Ressources

import { preload, preinit, prefetchDNS, preconnect } from 'react-dom';

function App() {
  // Précharger des ressources critiques
  preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2' });
  preinit('/scripts/analytics.js', { as: 'script' });
  prefetchDNS('https://api.exemple.com');
  preconnect('https://cdn.exemple.com');

  return <MainContent />;
}

Support des Styles et Feuilles de Style

Priorité des Styles

function Dashboard() {
  return (
    <>
      {/* Priorité basse - chargé après les autres */}
      <link rel="stylesheet" href="/styles/base.css" precedence="low" />

      {/* Priorité moyenne */}
      <link rel="stylesheet" href="/styles/components.css" precedence="medium" />

      {/* Priorité haute - chargé en premier */}
      <link rel="stylesheet" href="/styles/critical.css" precedence="high" />

      <DashboardContent />
    </>
  );
}

Migration vers React 19 : Guide Pratique

Étape 1 : Mise à Jour des Dépendances

# Mise à jour de React
npm install react@19 react-dom@19

# Mise à jour des types TypeScript
npm install -D @types/react@19 @types/react-dom@19

# Si vous utilisez Next.js
npm install next@15

Étape 2 : Vérification de la Compatibilité

// Activer le mode strict pour détecter les problèmes
import { StrictMode } from 'react';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>
);

Étape 3 : Migration Progressive des Formulaires

// Identifiez vos formulaires existants et migrez-les progressivement
// Commencez par les formulaires simples

// Avant
function SimpleForm() {
  const [value, setValue] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    setLoading(true);
    await submitData(value);
    setLoading(false);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={value} onChange={e => setValue(e.target.value)} />
      <button disabled={loading}>{loading ? 'Envoi...' : 'Envoyer'}</button>
    </form>
  );
}

// Après
function SimpleForm() {
  async function handleSubmit(formData: FormData) {
    await submitData(formData.get('value'));
  }

  return (
    <form action={handleSubmit}>
      <input name="value" />
      <SubmitButton />
    </form>
  );
}

Bonnes Pratiques React 19

1. Privilégiez les Actions pour les Formulaires

// ✅ Recommandé
<form action={submitAction}>
  <input name="email" />
  <button type="submit">Envoyer</button>
</form>

// ❌ Évitez l'ancien pattern quand possible
<form onSubmit={handleSubmit}>

2. Utilisez useOptimistic pour l'UX

// ✅ Retour instantané pour l'utilisateur
const [optimisticData, updateOptimistic] = useOptimistic(data, reducer);

// ❌ Attendre la réponse serveur dégrade l'expérience

3. Server Components par Défaut

// ✅ Server Component - pas de directive
async function DataDisplay() {
  const data = await fetchData();
  return <div>{data}</div>;
}

// Client uniquement quand nécessaire
'use client';
function InteractiveWidget() { /* ... */ }

Conclusion

React 19 représente une évolution majeure qui simplifie considérablement le développement d'applications modernes. Les Actions transforment la gestion des formulaires, useOptimistic améliore l'expérience utilisateur, et le React Compiler élimine le besoin de mémoïsation manuelle.

Les points clés à retenir :

✅ Actions : Simplification radicale des formulaires avec useFormStatus et useActionState ✅ use() : Lecture de promesses et contextes avec flexibilité ✅ useOptimistic : Interfaces utilisateur instantanées et réactives ✅ Server Components : Moins de JavaScript, meilleures performances ✅ React Compiler : Optimisation automatique, code plus naturel ✅ Métadonnées natives : Gestion simplifiée du document head

Ces nouvelles fonctionnalités ne sont pas seulement des améliorations techniques. Elles changent fondamentalement la façon dont nous construisons des applications React, en réduisant la complexité et en améliorant à la fois l'expérience développeur et utilisateur.

Conseil pratique : Commencez par migrer un petit formulaire avec les Actions, puis adoptez progressivement les autres fonctionnalités. La transition vers React 19 peut se faire de manière incrémentale.


Besoin d'aide pour migrer votre application vers React 19 ? Contactez Raicode pour discuter de vos besoins.

Partager :

Prêt à lancer votre projet ?

Transformez vos idées en réalité avec un développeur passionné par la performance et le SEO. Discutons de votre projet dès aujourd'hui.

Demander un devis
Voir mes réalisations
Réponse < 48h
15+ projets livrés
100% satisfaction client

Table des matières

Articles similaires

Next.js 15 : App Router, Server Actions et les Nouvelles Fonctionnalités à Connaître
development

Next.js 15 : App Router, Server Actions et les Nouvelles Fonctionnalités à Connaître

5 décembre 2025
12 min read
TypeScript 5.x : Types Avancés et Patterns Modernes pour des Applications Robustes
development

TypeScript 5.x : Types Avancés et Patterns Modernes pour des Applications Robustes

5 décembre 2025
13 min read
TypeScript vs JavaScript : Pourquoi TypeScript est l'Avenir du Développement Web
development

TypeScript vs JavaScript : Pourquoi TypeScript est l'Avenir du Développement Web

3 décembre 2025
13 min read
RAICODE

Développeur Full-Stack spécialisé en Next.js & React.
Je crée des applications web performantes et sur mesure.

SERVICES

  • Sites Vitrines
  • Applications SaaS
  • E-commerce
  • API & Backend

NAVIGATION

  • Processus
  • Projets
  • Blog
  • Tarifs
  • Contact

LÉGAL

  • Mentions légales
  • Confidentialité
  • CGU
  • CGV

© 2025 Raicode. Tous droits réservés.

Créé parRaicode.
↑ Retour en haut