Inertia + React sous Laravel 12 : retour d'expérience
Laravel 12 intègre un starter kit React basé sur Inertia.js. Cette stack offre le meilleur des deux mondes : routing et controllers côté serveur, UI réactive côté client. Voici mon retour après plusieurs projets en production.
C'est quoi Inertia ?
Inertia n'est pas un framework frontend. C'est un adaptateur qui connecte votre backend Laravel à votre frontend React (ou Vue, Svelte). Pas d'API REST à construire, pas de gestion d'état complexe.
Le principe
Vous écrivez vos controllers Laravel normalement, mais au lieu de retourner une vue Blade, vous retournez un composant React avec ses props. Inertia gère la navigation SPA-like sans rechargement de page.
app/Http/Controllers/UserController.php
use Inertia\Inertia;
class UserController extends Controller
{
public function show(User $user): Response
{
return Inertia::render('Users/Show', [
'user' => $user,
'posts' => $user->posts()->latest()->get(),
]);
}
}
resources/js/Pages/Users/Show.jsx
import Layout from '@/Layouts/Authenticated';
import { Head, Link } from '@inertiajs/react';
export default function Show({ user, posts }) {
return (
<Layout>
<Head title={user.name} />
<h1>{user.name}</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<Link href={`/posts/${post.id}`}>
{post.title}
</Link>
</li>
))}
</ul>
</Layout>
);
}
Setup avec Laravel 12
Laravel 12 propose un starter kit React officiel via laravel new.
Terminal
# Nouveau projet avec le starter React
laravel new my-app
# Choisir "React with Inertia" dans le menu interactif
# Ou directement :
laravel new my-app --stack=react
Le starter inclut tout : authentification, layouts, TypeScript (optionnel), Tailwind CSS.
Structure des fichiers
Structure Inertia/React
resources/js/
├── app.jsx # Point d'entrée
├── Pages/
│ ├── Dashboard.jsx
│ ├── Auth/
│ │ ├── Login.jsx
│ │ └── Register.jsx
│ └── Users/
│ ├── Index.jsx
│ └── Show.jsx
├── Components/
│ ├── Button.jsx
│ └── Input.jsx
└── Layouts/
├── Authenticated.jsx
└── Guest.jsx
Configuration du point d'entrée
resources/js/app.jsx
import { createInertiaApp } from '@inertiajs/react';
import { createRoot } from 'react-dom/client';
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true });
return pages[`./Pages/${name}.jsx`];
},
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />);
},
});
Partage de données globales
Certaines données doivent être disponibles sur toutes les pages : utilisateur connecté, permissions, flash messages. Le middleware HandleInertiaRequests gère ça.
app/Http/Middleware/HandleInertiaRequests.php
class HandleInertiaRequests extends Middleware
{
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user(),
'permissions' => $request->user()?->getAllPermissions()
->pluck('name'),
],
'flash' => [
'success' => session('success'),
'error' => session('error'),
],
];
}
}
Accès côté React
import { usePage } from '@inertiajs/react';
export default function Dashboard() {
const { auth, flash } = usePage().props;
return (
<div>
{flash.success && (
<div className="alert-success">{flash.success}</div>
)}
<p>Bienvenue, {auth.user.name}</p>
</div>
);
}
Formulaires avec Inertia
Le hook useForm simplifie la gestion des formulaires : état, validation, soumission.
resources/js/Pages/Posts/Create.jsx
import { useForm } from '@inertiajs/react';
export default function Create() {
const { data, setData, post, processing, errors } = useForm({
title: '',
content: '',
});
function submit(e) {
e.preventDefault();
post('/posts');
}
return (
<form onSubmit={submit}>
<div>
<input
type="text"
value={data.title}
onChange={e => setData('title', e.target.value)}
/>
{errors.title && <span>{errors.title}</span>}
</div>
<div>
<textarea
value={data.content}
onChange={e => setData('content', e.target.value)}
/>
{errors.content && <span>{errors.content}</span>}
</div>
<button type="submit" disabled={processing}>
Créer
</button>
</form>
);
}
Les erreurs de validation Laravel sont automatiquement disponibles dans errors.
Server-Side Rendering (SSR)
Pour le SEO et les performances perçues, Inertia supporte le SSR. Laravel 12 le configure automatiquement.
Terminal
# Build avec SSR
npm run build:ssr
# Lancer le serveur SSR en dev
php artisan inertia:start-ssr
# Ou tout en un
composer dev:ssr
resources/js/ssr.jsx
import { createInertiaApp } from '@inertiajs/react';
import createServer from '@inertiajs/react/server';
import ReactDOMServer from 'react-dom/server';
createServer(page =>
createInertiaApp({
page,
render: ReactDOMServer.renderToString,
resolve: name => {
const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true });
return pages[`./Pages/${name}.jsx`];
},
setup: ({ App, props }) => <App {...props} />,
}),
);
Quand activer le SSR
Activez-le si le SEO est important (landing pages, blog, e-commerce public).
Pas nécessaire pour les dashboards et apps internes où tout est derrière auth.
Inertia vs Livewire : lequel choisir ?
Les deux sont des solutions officielles Laravel. Le choix dépend de votre contexte.
Choisir Inertia + React quand :
- Équipe frontend expérimentée en React/Vue
- UI complexes : drag & drop, animations, éditeurs rich text
- Écosystème React : vous voulez utiliser des libs React spécifiques
- TypeScript : meilleur support que Livewire
- Offline-first : PWA avec service workers
Choisir Livewire quand :
- Équipe PHP/Laravel sans expertise frontend
- Prototypage rapide : moins de boilerplate
- CRUD classiques : formulaires, tableaux, filtres
- Real-time simple : notifications, updates live
- Budget serré : un seul développeur full-stack
Mon avis : Pour les apps métier complexes avec beaucoup d'interactivité, Inertia + React offre plus de flexibilité. Pour les CRUD et dashboards, Livewire est souvent plus productif.
Patterns utiles
Layouts persistants
Évitez de re-render le layout entier à chaque navigation.
Layout persistant
import AppLayout from '@/Layouts/AppLayout';
function Dashboard({ stats }) {
return (
<div>
<h1>Dashboard</h1>
<div>Active Users: {stats.activeUsers}</div>
</div>
);
}
// Le layout persiste entre les navigations
Dashboard.layout = page => <AppLayout children={page} />;
export default Dashboard;
Partial reloads
Rechargez seulement certaines props pour optimiser les performances.
Partial reload
import { router } from '@inertiajs/react';
// Recharge seulement 'notifications' sans toucher au reste
router.reload({ only: ['notifications'] });
// Avec polling automatique
setInterval(() => {
router.reload({ only: ['notifications'] });
}, 30000);
Preserving state
Préserver le scroll et l'état
import { Link } from '@inertiajs/react';
// Préserve la position de scroll
<Link href="/users" preserveScroll>Users</Link>
// Préserve l'état local du composant
<Link href="/users" preserveState>Users</Link>
Pièges à éviter
Ne pas exposer trop de données
Les props sont sérialisées en JSON et envoyées au client. N'envoyez pas de données sensibles (mots de passe hashés, tokens, données privées d'autres users).
Filtrer les données sensibles
// Mauvais : expose tout le user
return Inertia::render('Profile', [
'user' => $user, // Inclut remember_token, etc.
]);
// Bon : sélectionne les champs
return Inertia::render('Profile', [
'user' => $user->only(['id', 'name', 'email', 'avatar']),
]);
// Mieux : utilisez une API Resource
return Inertia::render('Profile', [
'user' => new UserResource($user),
]);
Attention aux relations non chargées
N+1 avec Inertia
// Mauvais : N+1 lors de la sérialisation
return Inertia::render('Posts/Index', [
'posts' => Post::all(), // author sera lazy-loaded
]);
// Bon : eager load
return Inertia::render('Posts/Index', [
'posts' => Post::with('author')->get(),
]);
Conclusion
Inertia + React est une excellente stack pour les applications Laravel modernes. Elle combine la productivité du développement server-side avec la richesse de l'écosystème React.
Points clés à retenir :
- Pas d'API à construire : vos controllers retournent directement des composants
- Validation Laravel classique avec erreurs automatiques côté React
- SSR optionnel pour le SEO
- Layouts persistants pour éviter les re-renders
- Attention aux données exposées dans les props