Module 7 – Données partagées (shared data)
Niveau 5.2 – Laravel Inertia (React & Vue)
Objectif
À la fin de ce module vous saurez :
- Partager des données globales avec toutes les pages Inertia (utilisateur connecté, messages flash, permissions, config) sans les repasser dans chaque
Inertia::render(). - Configurer la méthode share() du middleware HandleInertiaRequests.
- Lire ces données côté React et Vue avec usePage().props et les utiliser dans les layouts (navbar, bandeaux de notification).
Nous détaillons la configuration Laravel puis des exemples complets d’affichage (auth, flash) en React et Vue.
Pourquoi des « shared data » ?
Certaines données sont communes à toutes les pages : l’utilisateur connecté (pour afficher son nom dans la navbar, ou masquer des liens selon les droits), les messages flash (succès après création, erreur après échec), ou des réglages d’application (nom de l’app, feature flags). Si vous les passiez uniquement via Inertia::render('Page', [ ... ]), vous devriez les ajouter dans chaque contrôleur, ce qui est répétitif et source d’oubli. Inertia permet de définir des données partagées dans le middleware HandleInertiaRequests : elles sont automatiquement ajoutées à chaque réponse Inertia. Côté client, elles sont disponibles dans usePage().props sur toutes les pages, donc vous pouvez les utiliser dans un layout une seule fois et elles seront à jour partout.
Middleware HandleInertiaRequests – méthode share()
Le middleware est créé lors de l’installation d’Inertia Laravel (voir module 2). Ouvrez app/Http/Middleware/HandleInertiaRequests.php. La méthode share(Request $request): array doit retourner un tableau de clés qui seront fusionnées avec les props de chaque réponse Inertia.
Exemple complet :
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
protected $rootView = 'app';
public function version(Request $request): ?string
{
return parent::version($request);
}
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' => fn () => $request->session()->get('success'),
'error' => fn () => $request->session()->get('error'),
'warning' => fn () => $request->session()->get('warning'),
],
]);
}
}
Explication :
- auth.user : si un utilisateur est connecté (
$request->user()), on expose uniquement id, name, email (éviter d’exposer des champs sensibles). Sinon null. Sur chaque page, props.auth.user sera donc l’utilisateur ou null. - flash : les messages flash sont stockés en session Laravel. On utilise des closures (
fn () => ...) pour que la valeur soit lue au moment de la réponse (les flash sont souvent mis juste avant la redirect). Ainsi props.flash.success, props.flash.error, props.flash.warning contiendront le message de la session, s’il y en a un.
Côté contrôleur – pour définir un message flash après une action :
return redirect()->route('users.index')->with('success', 'Utilisateur créé.');
return redirect()->back()->with('error', 'Une erreur est survenue.');
Ces messages seront disponibles une seule requête (lecture puis consommation par Laravel). Après affichage côté client, vous pouvez les « consommer » ou les laisser disparaître au prochain chargement.
Accéder aux shared data – React
Le hook usePage() (fourni par @inertiajs/react) retourne l’objet page courant ; page.props contient à la fois les props spécifiques de la page (passées par Inertia::render) et les shared data (auth, flash, etc.).
Exemple – Layout avec utilisateur et déconnexion :
import React from 'react';
import { Link, usePage } from '@inertiajs/react';
export default function AppLayout({ children }) {
const { props } = usePage();
const user = props.auth?.user;
return (
<div>
<nav>
<Link href="/">Accueil</Link>
<Link href="/dashboard">Dashboard</Link>
{user ? (
<>
<span className="ml-4">Bonjour, {user.name}</span>
<Link
href={route('logout')}
method="post"
as="button"
className="ml-2"
>
Déconnexion
</Link>
</>
) : (
<Link href="/login">Connexion</Link>
)}
</nav>
<main>{children}</main>
</div>
);
}
- props.auth?.user : évite une erreur si auth ou user est undefined (première visite sans auth, ou config pas encore en place).
- Link avec method="post" et as="button" : pour la déconnexion (route POST /logout), Inertia envoie une requête POST ; as="button" rend un bouton au lieu d’un lien (pour le style ou l’accessibilité).
Exemple – Bandeau flash success / error :
import { usePage } from '@inertiajs/react';
export default function AppLayout({ children }) {
const { props } = usePage();
const flash = props.flash || {};
return (
<div>
{flash.success && (
<div className="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded">
{flash.success}
</div>
)}
{flash.error && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded">
{flash.error}
</div>
)}
{children}
</div>
);
}
Dès qu’un contrôleur fait redirect()->with('success', '...'), la page suivante aura props.flash.success rempli ; vous l’affichez une fois, et au prochain clic (nouvelle requête) le flash aura été consommé côté serveur, donc flash.success sera à nouveau vide.
Accéder aux shared data – Vue
Même principe avec usePage() (fourni par @inertiajs/vue3). Comme les props peuvent changer à chaque navigation, il est pratique d’utiliser computed pour que le template réagisse correctement.
Exemple – Layout avec utilisateur :
<template>
<div>
<nav>
<Link href="/">Accueil</Link>
<Link href="/dashboard">Dashboard</Link>
<template v-if="user">
<span class="ml-4">Bonjour, {{ user.name }}</span>
<Link
:href="route('logout')"
method="post"
as="button"
class="ml-2"
>
Déconnexion
</Link>
</template>
<Link v-else href="/login">Connexion</Link>
</nav>
<main><slot /></main>
</div>
</template>
<script setup>
import { Link, usePage } from '@inertiajs/vue3';
import { computed } from 'vue';
const page = usePage();
const user = computed(() => page.props.auth?.user);
</script>
Exemple – Bandeaux flash :
<template>
<div>
<div
v-if="flash.success"
class="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded"
>
{{ flash.success }}
</div>
<div
v-if="flash.error"
class="bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded"
>
{{ flash.error }}
</div>
<slot />
</div>
</template>
<script setup>
import { usePage } from '@inertiajs/vue3';
import { computed } from 'vue';
const page = usePage();
const flash = computed(() => page.props.flash || {});
</script>
Aller plus loin : permissions, feature flags
Vous pouvez exposer des permissions ou des feature flags dans share() pour que le front puisse afficher ou masquer des liens / boutons sans refaire un appel API. Exemple :
public function share(Request $request): array
{
return array_merge(parent::share($request), [
'auth' => [
'user' => $request->user()?->only(['id', 'name', 'email']),
'can' => [
'manageUsers' => $request->user()?->can('manage', User::class),
],
],
]);
}
Côté React : props.auth.can.manageUsers (booléen). Vous pouvez alors afficher le lien « Utilisateurs » seulement si can.manageUsers est true. Cela évite d’exposer des routes sensibles à des utilisateurs non autorisés (le backend doit bien sûr refuser l’accès en middleware/policy ; le front ne fait que de l’UX).
À retenir
- HandleInertiaRequests::share() : retourne un tableau de clés ajoutées à chaque réponse Inertia (auth, flash, etc.).
- auth.user : utilisateur connecté (ou null) ; utiliser des only([...]) pour limiter les champs exposés.
- flash : utiliser des closures pour lire la session au moment de la réponse ; with('success', '...') dans les redirects Laravel.
- Côté client : usePage().props (React) ou usePage().props + computed (Vue) pour accéder à auth et flash dans les layouts et les pages.
- Permissions / feature flags : les exposer dans share() pour piloter l’affichage (liens, boutons) sans appel API supplémentaire.
Dans le prochain module, nous verrons la gestion d’état côté client (React : useState, Context ; Vue : ref, Pinia) et quand garder l’état local vs côté serveur.