> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction à React — bases pour l'utiliser avec Inertia × Laravel

> Guide d'introduction à React avec Laravel + Inertia.js. Historique de laravel/ui jusqu'aux starter kits actuels et explication pratique des hooks React d'Inertia comme useForm ou usePage.

## Qu'est-ce que React ?

React est une bibliothèque JavaScript développée et maintenue par Meta (ex-Facebook) pour construire des interfaces utilisateur. Elle se caractérise par une écriture d'UI **déclarative** et une architecture **basée sur des composants**, utilisée aussi bien pour de petits widgets que pour des SPA complètes.

Le cœur de React est le rendu efficace via un **DOM virtuel**. Quand l'état (state) change, React applique uniquement le diff au DOM, sans que vous manipuliez le DOM manuellement.

<Info>
  Cette page traite de React 19 combiné à Inertia v3. Les starter kits Laravel 13 utilisent cette configuration par défaut.
</Info>

### JSX et TSX

Les composants React s'écrivent en **JSX** (JavaScript XML), une syntaxe proche du HTML directement intégrée à JavaScript.

```jsx theme={null}
// Exemple JSX
function Greeting({ name }) {
    return <h1>Bonjour {name}</h1>
}
```

Les starter kits utilisent **TypeScript** (`.tsx`) par défaut. Les définitions de types renforcent la complétion IDE et permettent de détecter les bugs plus tôt.

```tsx theme={null}
// Exemple TSX (TypeScript)
type Props = {
    name: string
}

function Greeting({ name }: Props) {
    return <h1>Bonjour {name}</h1>
}
```

<Tip>
  Le starter kit React de Laravel utilise TypeScript + TSX par défaut. Tous les exemples de cette page sont écrits en TSX.
</Tip>

***

## Place de React dans Laravel

### Historique

React est arrivé un peu plus tard que Vue dans l'histoire de Laravel, mais il est aujourd'hui à parité ou davantage.

```mermaid theme={null}
timeline
    title Historique Laravel et React
    2019 : Laravel 6 — scaffold React dans laravel/ui
    2021 : Laravel 8 — Breeze ajoute la stack Inertia React
    2022 : Laravel 9 — passage à Vite
    2025 : Laravel 12 — refonte des starter kits (React / Vue)
    2026 : Laravel 13 — starter kits compatibles Inertia v3
```

Avec **Laravel 6 (2019)**, le scaffold d'authentification est extrait dans le package `laravel/ui` et une version React est proposée en même temps que Vue. À l'époque, Vue reste dominant et React est en retrait.

Avec **Laravel Breeze (2021)**, la stack Inertia + React est ajoutée, ce qui marque le vrai début de son adoption. La refonte des starter kits de **Laravel 12 (2025)** met React totalement à égalité avec Vue (voire en tête dans l'affichage).

### Style dominant actuel : Inertia × React

Aujourd'hui, l'usage principal de React avec Laravel se fait via **Inertia × React**. Inertia réalise une architecture « monolithe moderne » où l'on passe directement des données du contrôleur Laravel à un composant React, sans concevoir d'API.

```mermaid theme={null}
graph LR
    Browser["Navigateur"]
    Inertia["Inertia.js<br>(adaptateur)"]
    Laravel["Laravel<br>(contrôleur)"]
    React["React<br>(composant de page)"]

    Browser <-->|XHR / rechargement complet| Inertia
    Inertia <-->|Réponse Inertia| Laravel
    Inertia -->|props| React
    React -->|rendu| Browser
```

***

## Mise en place

### Via un starter kit (recommandé)

Pour un nouveau projet, le plus simple est de partir d'un starter kit.

```shell theme={null}
laravel new my-app
```

En choisissant **React** dans le prompt interactif, tout est configuré automatiquement :

* `inertiajs/inertia-laravel` (adaptateur serveur)
* `@inertiajs/react` (adaptateur client)
* `react` + `react-dom` (React 19)
* `@vitejs/plugin-react` (plugin Vite)
* TypeScript + `@types/react`
* Tailwind CSS + shadcn/ui
* Middleware `HandleInertiaRequests`
* Écrans d'authentification (login, register, etc.) déjà implémentés en Inertia + React + TypeScript

### Installation manuelle

Pour un projet existant, installez côté serveur et côté client séparément.

```shell theme={null}
# Côté serveur (PHP)
composer require inertiajs/inertia-laravel

# Côté client (JavaScript)
npm install @inertiajs/react react react-dom
npm install --save-dev @vitejs/plugin-react @types/react @types/react-dom typescript
```

Ajoutez ensuite le plugin React à `vite.config.ts`.

```ts theme={null}
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import react from '@vitejs/plugin-react'

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.tsx'],
            refresh: true,
        }),
        react(),
    ],
})
```

Démarrez l'application Inertia dans `resources/js/app.tsx`.

```tsx theme={null}
import { createInertiaApp } from '@inertiajs/react'
import { createRoot } from 'react-dom/client'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'

createInertiaApp({
    resolve: (name) =>
        resolvePageComponent(
            `./pages/${name}.tsx`,
            import.meta.glob('./pages/**/*.tsx'),
        ),
    setup({ el, App, props }) {
        createRoot(el).render(<App {...props} />)
    },
})
```

<Info>
  Pour les détails d'une installation manuelle (template racine, enregistrement du middleware, etc.), consultez la [documentation officielle Inertia](https://inertiajs.com/installation).
</Info>

***

## Structure des répertoires

Dans le starter kit, les composants de page React se trouvent dans `resources/js/pages/`.

```
resources/js/
├── app.tsx            # Point d'entrée de l'application Inertia
├── bootstrap.ts
├── components/        # Composants UI réutilisables
│   ├── ui/            # Composants shadcn/ui
│   └── ...
├── hooks/             # Hooks React personnalisés
├── layouts/           # Composants de layout
│   ├── app-layout.tsx
│   └── auth-layout.tsx
├── lib/               # Utilitaires et configuration
├── pages/             # Composants de page Inertia (correspondent au nom de contrôleur)
│   ├── auth/
│   │   ├── login.tsx
│   │   └── register.tsx
│   ├── dashboard.tsx
│   └── posts/
│       ├── index.tsx
│       ├── create.tsx
│       └── show.tsx
└── types/             # Définitions de types TypeScript
```

`Inertia::render('posts/index', [...])` correspond au composant `resources/js/pages/posts/index.tsx`.

***

## Bases de la syntaxe JSX

React utilise JSX : une syntaxe façon HTML au sein de JavaScript. Voici les patrons minimaux à connaître pour lire le code des starter kits.

#### `{}` — interpolation de variable

Dans JSX, `{}` permet d'injecter une valeur ou une expression JavaScript.

```tsx theme={null}
const name = 'le monde'
const count = 3

return (
    <div>
        <p>Bonjour, {name} !</p>
        <p>Le double : {count * 2}</p>
    </div>
)
```

#### Branchement — `&&` et opérateur ternaire

React n'a pas d'équivalent de `v-if`. Pour un simple test, utilisez `&&`. Pour un if/else, l'opérateur ternaire `? :`.

```tsx theme={null}
{/* Affiché uniquement si vrai */}
{isLoggedIn && <p>Bienvenue !</p>}

{/* if / else */}
{isLoggedIn ? <p>Bienvenue !</p> : <a href="/login">Connexion</a>}

{/* if / else if / else */}
{isLoggedIn
    ? <p>Bienvenue !</p>
    : role === 'admin'
        ? <p>Connecté en tant qu'administrateur</p>
        : <a href="/login">Connexion</a>
}
```

#### Rendu de liste — `.map()`

Utilisez `Array.map()` pour rendre une liste. Précisez toujours la prop `key` pour un diff efficace.

```tsx theme={null}
<ul>
    {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
    ))}
</ul>
```

C'est l'équivalent du `v-for :key` de Vue et du `{#each}` de Svelte.

#### `className` — nom de classe CSS

JSX étant compilé en JavaScript, `class` est un mot-clé réservé. Pour les classes CSS, utilisez `className`.

```tsx theme={null}
{/* HTML: <div class="container"> */}
<div className="container">...</div>
```

#### Handlers d'événements — camelCase

Les attributs d'événements JSX sont en camelCase et reçoivent une référence de fonction.

```tsx theme={null}
<button onClick={handleClick}>Cliquer</button>

{/* Annuler le comportement par défaut de submit */}
<form onSubmit={(e) => { e.preventDefault(); handleSubmit() }}>
    ...
</form>
```

***

## Composant de page — bases

Un composant de page Inertia est un composant React classique. Les données passées depuis le contrôleur Laravel arrivent en props.

### Contrôleur

```php theme={null}
// app/Http/Controllers/PostController.php
use Inertia\Inertia;
use App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        return Inertia::render('posts/index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }
}
```

### Composant de page React

```tsx theme={null}
// resources/js/pages/posts/index.tsx
import { Link } from '@inertiajs/react'

type Post = {
    id: number
    title: string
    created_at: string
}

type Props = {
    posts: {
        data: Post[]
        // paginate(10) renvoie aussi les infos de pagination
        // current_page, last_page, per_page, total, etc.
    }
}

export default function PostsIndex({ posts }: Props) {
    return (
        <div>
            <h1>Liste des articles</h1>
            {posts.data.map((post) => (
                <article key={post.id}>
                    <h2>
                        <Link href={`/posts/${post.id}`}>{post.title}</Link>
                    </h2>
                    <p>{post.created_at}</p>
                </article>
            ))}
        </div>
    )
}
```

Il suffit de recevoir les props en argument du composant pour utiliser les données transmises par le contrôleur. Aucune API REST à définir.

***

## Composant `Link`

Le composant `<Link>` fourni par `@inertiajs/react` déclenche la navigation en XHR, évitant le rechargement complet du navigateur.

```tsx theme={null}
import { Link } from '@inertiajs/react'

export default function PostsIndex() {
    return (
        <div>
            {/* Lien de base */}
            <Link href="/posts">Liste des articles</Link>

            {/* Lien POST (suppression, etc.) */}
            <Link href="/posts/1" method="delete" as="button">
                Supprimer
            </Link>

            {/* Préchargement (préfetch au survol) */}
            <Link href="/posts/1" preload>
                Voir l'article
            </Link>
        </div>
    )
}
```

L'écriture est identique à celle d'une balise `<a>` classique, mais Inertia remplace uniquement le composant de page, offrant une expérience de type SPA.

***

## Composant `Form`

Le composant `<Form>` de `@inertiajs/react` est le style recommandé d'envoi de formulaire utilisé dans les écrans d'authentification des starter kits. On passe `action` et `method` en props et on récupère `errors` et `processing` via une fonction children (render prop).

### Utilisation de base

```tsx theme={null}
import { Form } from '@inertiajs/react'

export default function PostCreate() {
    return (
        <Form action="/posts" method="post" className="flex flex-col gap-4">
            {({ errors, processing }) => (
                <>
                    <div>
                        <label htmlFor="title">Titre</label>
                        <input id="title" name="title" type="text" required />
                        {errors.title && <p className="error">{errors.title}</p>}
                    </div>

                    <div>
                        <label htmlFor="content">Contenu</label>
                        <textarea id="content" name="content" />
                        {errors.content && <p className="error">{errors.content}</p>}
                    </div>

                    <button type="submit" disabled={processing}>
                        {processing ? 'Envoi...' : 'Publier'}
                    </button>
                </>
            )}
        </Form>
    )
}
```

Le children de `<Form>` est une fonction `({ errors, processing }) => JSX` (render prop). Le composant `Form` calcule et injecte ces valeurs. Les champs utilisent l'attribut natif `name` plutôt qu'un `onChange`, ce qui active la collecte standard des données de formulaire par le navigateur.

### Patron des starter kits

Les starter kits utilisent [Wayfinder](/fr/blog/wayfinder-introduction) pour gérer les routes en tant qu'objets. `store.form()` renvoie un objet contenant `action` et `method` que l'on spread dans `<Form>`.

```tsx theme={null}
import { Form } from '@inertiajs/react'
import { store } from '@/routes/login'

export default function Login() {
    return (
        <Form
            {...store.form()}
            resetOnSuccess={['password']}
            className="flex flex-col gap-6"
        >
            {({ errors, processing }) => (
                <>
                    {/* Contenu du formulaire */}
                </>
            )}
        </Form>
    )
}
```

Les champs listés dans `resetOnSuccess` sont automatiquement réinitialisés après un envoi réussi. Utilisez-le pour les champs comme le mot de passe qu'il faut vider.

<Info>
  Sans Wayfinder, passer directement une URL (`action="/login"`) fonctionne de la même façon.
</Info>

***

## Hook `useForm`

Pour la gestion de formulaire, utilisez le hook `useForm` de `@inertiajs/react`. Il simplifie la gestion d'état, l'envoi et l'affichage des erreurs de validation.

### Côté contrôleur

```php theme={null}
// app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
        ]);

        Post::create($validated + ['user_id' => auth()->id()]);

        return redirect()->route('posts.index')
            ->with('success', 'Article créé.');
    }
}
```

### Composant de formulaire React

```tsx theme={null}
// resources/js/pages/posts/create.tsx
import { useForm } from '@inertiajs/react'
import { FormEventHandler } from 'react'

export default function PostCreate() {
    const { data, setData, post, processing, errors } = useForm({
        title: '',
        content: '',
    })

    const submit: FormEventHandler = (e) => {
        e.preventDefault()
        post('/posts')
    }

    return (
        <form onSubmit={submit}>
            <div>
                <label>Titre</label>
                <input
                    type="text"
                    value={data.title}
                    onChange={(e) => setData('title', e.target.value)}
                />
                {errors.title && <p className="error">{errors.title}</p>}
            </div>

            <div>
                <label>Contenu</label>
                <textarea
                    value={data.content}
                    onChange={(e) => setData('content', e.target.value)}
                />
                {errors.content && <p className="error">{errors.content}</p>}
            </div>

            <button type="submit" disabled={processing}>
                {processing ? 'Envoi...' : 'Publier'}
            </button>
        </form>
    )
}
```

Principales propriétés retournées par `useForm`.

| Propriété / Méthode     | Description                                        |
| ----------------------- | -------------------------------------------------- |
| `data`                  | Objet des données du formulaire                    |
| `setData(field, value)` | Met à jour la valeur d'un champ                    |
| `errors`                | Erreurs de validation (par nom de champ)           |
| `processing`            | `true` pendant l'envoi (pour désactiver le bouton) |
| `isDirty`               | `true` si la valeur diffère de l'initiale          |
| `post(url)`             | Envoi en POST                                      |
| `put(url)`              | Envoi en PUT (mise à jour)                         |
| `delete(url)`           | Envoi en DELETE                                    |
| `reset()`               | Réinitialiser le formulaire aux valeurs initiales  |

En cas d'erreur de validation, `useForm` conserve les valeurs saisies tout en affichant les erreurs.

***

## Données partagées (Shared Data)

Les données communes à toutes les pages (utilisateur connecté, messages flash) sont déclarées dans la méthode `share()` du middleware `HandleInertiaRequests`.

```php theme={null}
// app/Http/Middleware/HandleInertiaRequests.php
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'auth' => [
                'user' => $request->user()
                    ? $request->user()->only('id', 'name', 'email')
                    : null,
            ],
            'flash' => [
                'success' => $request->session()->get('success'),
                'error'   => $request->session()->get('error'),
            ],
        ]);
    }
}
```

Depuis un composant React, accédez aux données partagées via le hook `usePage()`.

```tsx theme={null}
import { usePage } from '@inertiajs/react'

type SharedProps = {
    auth: {
        user: { id: number; name: string; email: string } | null
    }
    flash: {
        success: string | null
        error: string | null
    }
}

export default function AppHeader() {
    const { auth, flash } = usePage<SharedProps>().props

    return (
        <>
            <header>
                {auth.user ? (
                    <span>{auth.user.name}</span>
                ) : (
                    <span>Invité</span>
                )}
            </header>

            {flash.success && (
                <div className="alert-success">{flash.success}</div>
            )}
        </>
    )
}
```

<Info>
  Ces données étant présentes à chaque requête, limitez-les au strict nécessaire. Une évaluation paresseuse avec `fn()` ne les calcule qu'en cas d'accès effectif.
</Info>

***

## Bases des hooks React

Voici les hooks essentiels pour développer avec Inertia × React.

### `useState` — état local

```tsx theme={null}
import { useState } from 'react'

export default function Counter() {
    const [count, setCount] = useState(0)
    const [isOpen, setIsOpen] = useState(false)

    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <button onClick={() => setIsOpen(!isOpen)}>Basculer</button>
        </div>
    )
}
```

### `useEffect` — effets de bord

```tsx theme={null}
import { useState, useEffect } from 'react'

export default function Timer() {
    const [seconds, setSeconds] = useState(0)

    useEffect(() => {
        const timer = setInterval(() => {
            setSeconds((s) => s + 1)
        }, 1000)

        // Fonction de nettoyage
        return () => clearInterval(timer)
    }, []) // Tableau vide = exécuté une seule fois au montage

    return <p>Temps écoulé : {seconds} s</p>
}
```

### `useMemo` et `useCallback` — optimisation de performance

```tsx theme={null}
import { useMemo, useCallback } from 'react'
import { router } from '@inertiajs/react'

type Post = { id: number; title: string; published: boolean }
type Props = { posts: Post[] }

export default function PostsList({ posts }: Props) {
    // Mémoïse une valeur (recalculée seulement si posts change)
    const publishedPosts = useMemo(
        () => posts.filter((post) => post.published),
        [posts],
    )

    // Mémoïse une fonction (recréée seulement si ses dépendances changent)
    const handleClick = useCallback((id: number) => {
        router.visit(`/posts/${id}`)
    }, [])

    return (
        <ul>
            {publishedPosts.map((post) => (
                <li key={post.id} onClick={() => handleClick(post.id)}>
                    {post.title}
                </li>
            ))}
        </ul>
    )
}
```

***

## Support TypeScript

Le starter kit React utilise TypeScript par défaut. Combiné aux types Inertia, il assure la sûreté des props.

### Types globaux

Le starter kit définit le type des données partagées dans `resources/js/types/index.d.ts`.

```ts theme={null}
// resources/js/types/index.d.ts
export interface User {
    id: number
    name: string
    email: string
    email_verified_at?: string
}

export type PageProps<T extends Record<string, unknown> = Record<string, unknown>> = T & {
    auth: {
        user: User
    }
}
```

### Utilisation du type dans un composant de page

```tsx theme={null}
import { PageProps } from '@/types'

type Post = {
    id: number
    title: string
    content: string
}

export default function PostsIndex({ auth, posts }: PageProps<{ posts: Post[] }>) {
    return (
        <div>
            <p>Connecté : {auth.user.name}</p>
            {posts.map((post) => (
                <article key={post.id}>
                    <h2>{post.title}</h2>
                </article>
            ))}
        </div>
    )
}
```

***

## Conclusion

React s'exprime particulièrement bien avec Laravel dans un « monolithe moderne » via Inertia. La bonne intégration avec TypeScript le rend adapté aux applications de grande taille.

| Élément                 | Rôle                                                  |
| ----------------------- | ----------------------------------------------------- |
| Contrôleur Laravel      | Routage, récupération de données, validation          |
| `Inertia::render()`     | Transmet les données du contrôleur au composant React |
| Composant de page React | Reçoit les props et rend l'UI                         |
| `useForm`               | Gestion d'état, envoi et erreurs de formulaire        |
| Composant `Link`        | Navigation sans rechargement complet                  |
| `usePage().props`       | Accès aux données partagées                           |
| TypeScript              | Sûreté des props et meilleure complétion IDE          |

Utiliser Inertia × React combine la simplicité du backend Laravel à la puissance de l'écosystème React. En partant du starter kit, vous pouvez démarrer le développement immédiatement, écrans d'authentification inclus.

<Card title="Documentation officielle Inertia.js" icon="book-open" href="https://inertiajs.com">
  Retrouvez toutes les fonctionnalités d'Inertia v3 dans la documentation officielle.
</Card>


## Related topics

- [Introduction à Svelte — bases pour l'utiliser avec Inertia × Laravel](/fr/blog/svelte-introduction.md)
- [Introduction à Vue.js — les bases pour l'utiliser avec Inertia × Laravel](/fr/blog/vue-introduction.md)
- [Frontend](/fr/frontend.md)
- [Construire une SPA avec Inertia.js](/fr/blog/inertia-introduction.md)
- [Introduction à Eloquent](/fr/eloquent.md)
