Module 3 – Routes, contrôleurs et pages
Niveau 5.2 – Laravel Inertia (React & Vue)
Objectif
À la fin de ce module vous saurez :
- Utiliser
Inertia::render()dans les contrôleurs pour envoyer une « page » et des props au client. - Recevoir et utiliser ces props dans les composants React et Vue (typage, structure).
- Organiser les fichiers de pages (dossiers, nommage) pour correspondre aux noms passés à
Inertia::render(). - Comprendre le cycle : URL → route → contrôleur → réponse Inertia → composant rendu.
Nous donnons des exemples complets Laravel + React et Laravel + Vue pour une même fonctionnalité (liste d’utilisateurs + détail).
Rappel : qui décide quelle page afficher ?
En Inertia, Laravel décide quelle page (composant) afficher. Chaque URL est gérée par une route Laravel. Le contrôleur exécute la logique (requêtes BDD, validation, etc.) puis appelle Inertia::render('NomDuComposant', [ ... props ... ]). Le nom du composant est le chemin du fichier sous resources/js/Pages/, sans extension.
Exemples de correspondance :
| Appel Laravel | Fichier côté client (React) | Fichier côté client (Vue) |
|---|---|---|
Inertia::render('Dashboard') | Pages/Dashboard.jsx | Pages/Dashboard.vue |
Inertia::render('Users/Index') | Pages/Users/Index.jsx | Pages/Users/Index.vue |
Inertia::render('Articles/Show') | Pages/Articles/Show.jsx | Pages/Articles/Show.vue |
Les props que vous passez en deuxième argument deviennent les props du composant React ou Vue. Tout ce qui est sérialisable en JSON (tableaux, objets, chaînes, nombres, booléens, null) peut être passé. Les modèles Eloquent sont automatiquement convertis en tableaux (ou en objets JSON) par Laravel.
Inertia::render() – signature et bonnes pratiques
Signature typique :
Inertia::render(string $component, array $props = [])
$component: nom de la page (chemin relatif àPages/, sans extension). Utilisez des sous-dossiers pour grouper :Users/Index,Users/Create,Users/Edit.$props: tableau associatif. Chaque clé devient le nom de la prop côté client.
Bonnes pratiques :
- Passer uniquement les données nécessaires à la page (éviter de tout envoyer « au cas où »).
- Pour les listes, passer la collection paginée (Laravel renvoie un objet avec
data,links,current_page, etc.) ; côté client vous afficherezusers.dataet les liens de pagination. - Pour les formulaires d’édition, passer la ressource (ex.
user) et éventuellement des listes pour les selects (rôles, etc.). - Ne pas passer d’objets non sérialisables (ressources, closures). Les modèles Eloquent sont automatiquement transformés (attributs visibles, ou via
$hidden,$appends, etc.).
Exemple complet : liste d’utilisateurs (Laravel)
Supposons un modèle User avec au moins id, name, email. Nous voulons une page liste et une page détail.
Routes
// routes/web.php
use App\Http\Controllers\UserController;
Route::get('/users', [UserController::class, 'index'])->name('users.index');
Route::get('/users/{user}', [UserController::class, 'show'])->name('users.show');
Contrôleur
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;
use App\Models\User;
use Inertia\Inertia;
class UserController extends Controller
{
public function index()
{
$users = User::query()
->orderBy('name')
->paginate(10);
return Inertia::render('Users/Index', [
'users' => $users,
]);
}
public function show(User $user)
{
return Inertia::render('Users/Show', [
'user' => $user,
]);
}
}
- index : on passe la pagination Laravel (
$users). Côté client vous recevrez un objet avecdata,links,current_page,last_page, etc. - show : on passe le modèle
$user. Laravel le sérialise en tableau (attributs du modèle).
Si vous voulez limiter les champs du modèle exposés, vous pouvez passer un tableau au lieu du modèle :
return Inertia::render('Users/Show', [
'user' => $user->only(['id', 'name', 'email', 'created_at']),
]);
Ou utiliser les API Resources Laravel pour un format cohérent (hors scope de ce module, mais très utile en projet réel).
Recevoir les props – React (Users/Index et Users/Show)
Page liste – resources/js/Pages/Users/Index.jsx
import React from 'react';
import { Link } from '@inertiajs/react';
export default function Index({ users }) {
return (
<div>
<h1>Liste des utilisateurs</h1>
<ul>
{users.data.map((user) => (
<li key={user.id}>
<Link href={`/users/${user.id}`}>
{user.name} – {user.email}
</Link>
</li>
))}
</ul>
{/* Liens de pagination simples */}
{users.prev_page_url && (
<Link href={users.prev_page_url}>Page précédente</Link>
)}
{users.next_page_url && (
<Link href={users.next_page_url}>Page suivante</Link>
)}
</div>
);
}
users: c’est l’objet paginé Laravel.users.dataest le tableau des éléments de la page courante.users.prev_page_url/users.next_page_urlsont des URLs prêtes à l’emploi pour la pagination.Link: composant Inertia pour la navigation sans rechargement. On l’utilise pour aller au détail d’un utilisateur et pour les liens de pagination.
Page détail – resources/js/Pages/Users/Show.jsx
import React from 'react';
import { Link } from '@inertiajs/react';
export default function Show({ user }) {
return (
<div>
<h1>Profil de {user.name}</h1>
<p>Email : {user.email}</p>
<p>Inscrit le : {new Date(user.created_at).toLocaleDateString()}</p>
<Link href="/users">Retour à la liste</Link>
</div>
);
}
user: objet (ou tableau) avec les champs du modèle (id, name, email, created_at, etc.). On l’utilise directement dans le JSX.
Typage TypeScript (React) – optionnel mais recommandé
En TypeScript, vous pouvez typer les props pour éviter les erreurs et avoir l’autocomplétion :
// Pages/Users/Index.tsx
import React from 'react';
import { Link } from '@inertiajs/react';
interface User {
id: number;
name: string;
email: string;
}
interface PaginatedUsers {
data: User[];
current_page: number;
last_page: number;
prev_page_url: string | null;
next_page_url: string | null;
}
interface Props {
users: PaginatedUsers;
}
export default function Index({ users }: Props) {
// ...
}
Recevoir les props – Vue (Users/Index et Users/Show)
Page liste – resources/js/Pages/Users/Index.vue
<template>
<div>
<h1>Liste des utilisateurs</h1>
<ul>
<li v-for="user in users.data" :key="user.id">
<Link :href="`/users/${user.id}`">
{{ user.name }} – {{ user.email }}
</Link>
</li>
</ul>
<Link v-if="users.prev_page_url" :href="users.prev_page_url">
Page précédente
</Link>
<Link v-if="users.next_page_url" :href="users.next_page_url">
Page suivante
</Link>
</div>
</template>
<script setup>
import { Link } from '@inertiajs/vue3';
defineProps({
users: {
type: Object,
required: true,
},
});
</script>
defineProps: définit les props du composant. Ici on attend un objetusers(l’objet paginé Laravel).Link: composant Inertia pour la navigation, importé depuis@inertiajs/vue3.
Page détail – resources/js/Pages/Users/Show.vue
<template>
<div>
<h1>Profil de {{ user.name }}</h1>
<p>Email : {{ user.email }}</p>
<p>Inscrit le : {{ formatDate(user.created_at) }}</p>
<Link href="/users">Retour à la liste</Link>
</div>
</template>
<script setup>
import { Link } from '@inertiajs/vue3';
defineProps({
user: {
type: Object,
required: true,
},
});
function formatDate(value) {
return value ? new Date(value).toLocaleDateString() : '';
}
</script>
Typage avec TypeScript (Vue) – optionnel
En Vue 3 avec <script setup lang="ts">, vous pouvez définir une interface pour les props :
<script setup lang="ts">
import { Link } from '@inertiajs/vue3';
interface User {
id: number;
name: string;
email: string;
created_at: string;
}
interface Props {
user: User;
}
defineProps<Props>();
function formatDate(value: string) {
return value ? new Date(value).toLocaleDateString() : '';
}
</script>
Structure des dossiers Pages/
Une organisation claire facilite la maintenance. Exemple :
resources/js/Pages/
├── Welcome.jsx → Inertia::render('Welcome')
├── Dashboard.jsx → Inertia::render('Dashboard')
├── Users/
│ ├── Index.jsx → Inertia::render('Users/Index')
│ ├── Show.jsx → Inertia::render('Users/Show')
│ ├── Create.jsx → Inertia::render('Users/Create')
│ └── Edit.jsx → Inertia::render('Users/Edit')
├── Articles/
│ ├── Index.jsx
│ ├── Show.jsx
│ └── ...
└── Settings/
└── Profile.jsx → Inertia::render('Settings/Profile')
Même logique en Vue avec des fichiers .vue. Gardez une convention de nommage (Index pour la liste, Show pour le détail, Create/Edit pour les formulaires) pour que tout le monde s’y retrouve.
Examen et erreurs courantes
- Page blanche ou « Component not found » : vérifier que le nom dans
Inertia::render('Users/Index')correspond bien au fichierPages/Users/Index.jsx(ou.vue), avec la bonne casse et les bons slashes. - Props undefined : vérifier que vous passez bien les clés dans le tableau de
Inertia::render()et que vous les lisez sous le même nom dans le composant (ex.userscôté Laravel →usersen prop). - Pagination : Laravel renvoie un objet avec
datapour les éléments. Toujours utiliserusers.datapour la liste etusers.links(ouprev_page_url/next_page_url) pour la pagination.
À retenir
Inertia::render('Nom/De/Page', [ 'prop1' => $value1, ... ]): nom = chemin sousPages/sans extension ; les clés du tableau deviennent les props du composant.- React : les props sont le premier argument de la fonction composant (ou définies via TypeScript).
- Vue : les props se déclarent avec
definePropset sont utilisées dans le template. - Pagination : passer le résultat de
->paginate(); côté client utiliser.datapour les éléments et.links/ prev_page_url / next_page_url pour la navigation. - Link (Inertia) pour tous les liens internes afin de garder la navigation en XHR.
Dans le prochain module, nous verrons comment créer des layouts (navbar, sidebar) et les réutiliser dans les pages React et Vue.