The problem: Data shared globally through HandleInertiaRequests — like the authenticated user or flash messages — stays stale after navigating to a new page, even though the server-side value has changed.
Environment: Laravel 11, Inertia.js 1.x, Vue 3 (also applies to React adapter), PHP 8.3
The Error
No JS error thrown.
$page.props.auth.user.name still shows the old value
after updating the profile and navigating to another page
via <Link>. Full page reload shows the correct value.
Why It Happens
Inertia's shared data (defined in HandleInertiaRequests::share()) is re-evaluated on every request by design — so if a full reload shows the correct value, the server-side data itself isn't the problem. The stale value you're seeing on client-side navigation almost always comes from one of these:
1. Partial reloads excluding shared props. If a page component uses only: [...] in a manual router.reload() or a lazy-loaded prop setup, Inertia only fetches the listed keys — shared props not explicitly requested don't get refreshed on that particular visit, so the old cached value in the frontend store just persists.
2. A Vuex/Pinia store or React context caches $page.props at mount time instead of reading reactively from Inertia's page object on every render — so even though Inertia's internal state updates correctly, your own store copy never re-syncs.
3. The shared data closure itself isn't lazy. If share() resolves the value once at boot instead of returning a closure, Laravel caches that computed value across the request lifecycle in a way that doesn't reflect changes made earlier in the same request (e.g., updating the user then redirecting back).
The Fix
First, make sure your shared data in HandleInertiaRequests is wrapped in a closure so it's evaluated fresh on every request, not memoized at boot:
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => fn () => $request->user()
? $request->user()->only('id', 'name', 'email')
: null,
],
'flash' => [
'message' => fn () => $request->session()->get('message'),
],
];
}
Next, if you're reading shared props into a Vuex/Pinia store or context on mount, switch to reading them reactively via usePage() instead of copying the value once:
- const user = usePage().props.auth.user; // captured once
+ import { computed } from 'vue';
+ import { usePage } from '@inertiajs/vue3';
+
+ const user = computed(() => usePage().props.auth.user);
If a partial reload is intentionally excluding shared props, add the shared keys explicitly to the only array, or use Inertia's always() wrapper so a prop is refetched regardless of partial reloads:
'flash' => [
'message' => Inertia::always(fn () => $request->session()->get('message')),
],
Step-by-Step
- Confirm the issue is client-side by comparing a full browser reload (correct value) against a
<Link>navigation (stale value). - Open
HandleInertiaRequests::share()and wrap every dynamic value in a closure (fn () => ...) instead of resolving it eagerly. - Search your frontend for any place that reads
usePage().propsonce at component setup and stores it in a local variable or global store. - Replace those with a
computed()(Vue) or context-driven read (React) that stays reactive to Inertia's page object. - For props you always want refreshed regardless of partial reload scope, wrap them in
Inertia::always(). - Re-test the original navigation flow and confirm the value updates without a full page reload.
Edge case: If you're using router.reload({ only: [...] }) anywhere for performance (skipping expensive props on frequent polling), remember that shared data is not automatically included unless it's wrapped in Inertia::always() — a performance optimization elsewhere in the app can silently cause this exact staleness bug on a completely unrelated page.
Still seeing stale shared data after all this? Reach out via the Contact page and I'll help debug your setup.