Managing State in Inertia.js: Persistent Layouts vs. Page Props
Learn when to use Persistent Layouts versus Page Props in Inertia.js to prevent UI state resets and optimize data payloads for better SPA performance.
22 Jul 2025, 06:24 UTC

The primary architectural challenge in Inertia.js is deciding where state should live. Because Inertia mimics a monolithic request-response cycle while rendering a Single Page Application (SPA), developers often struggle with "state reset." By default, every single navigation triggers a full page component remount, which destroys any local UI state—such as a playing audio file, a partially filled search bar, or a collapsed sidebar toggle.
To solve this, you must choose between using standard page props for data-driven state or implementing Persistent Layouts for UI-driven state. Choosing the wrong approach leads to either unnecessary server requests or a jarring user experience where the UI "flickers" or resets on every click.
Decision Matrix: State Management Strategies
| Strategy | Best Use Case | State Lifecycle | Performance Impact |
|---|---|---|---|
| Page Props | Database records, user permissions, page-specific settings. | Reset on every navigation. | Higher (requires server round-trip). |
| Persistent Layouts | Global navigation, media players, sidebar states. | Maintained across route changes. | Lower (client-side only). |
| useForm Hook | Input validation, submission loading states. | Scoped to the specific form. | Negligible. |
When to Use Persistent Layouts
A Persistent Layout is a wrapper component that does not remount when the page component inside it changes. This is critical for any element that must maintain a continuous state regardless of the URL. For example, if you have a music player in the footer, a standard Inertia layout would stop the music every time the user navigates to a new page because the entire component tree is destroyed and rebuilt.
Implementation Example (Vue 3)
To implement a persistent layout, define the layout as a property on the page component rather than wrapping the page content inside the template. This tells Inertia to keep the layout instance alive.
// Layout.vue
<template>
<div>
<nav>Global Navigation</nav>
<main><slot /></main>
<footer>Music Player: {{ currentTrack }}</footer>
</div>
</template>
// Page.vue
<script setup>
import Layout from './Layout.vue';
</script>
<template>
<div>Page Content</div>
</template>
<script>
export default {
layout: Layout,
}
</script>
The Risk of Prop Overload
While passing data via props is the standard Inertia way, it is easy to create "heavy payloads." If you pass a full User object with all its relationships (posts, comments, profile) to every page via a shared middleware, you increase the JSON size of every single request.
To mitigate this, use Partial Visits. This allows the client to request only a subset of the props, preventing the server from executing expensive queries for data that isn't changing.
Validation of Partial Visits
You can verify if a partial visit is working by monitoring the browser's Network tab. When using the only property in a visit, the server response should only contain the requested keys, not the full prop set.
// Client-side request
import { router } from '@inertiajs/vue3';
router.get('/users', { search: 'John' }, {
only: ['users'], // Only update the 'users' prop
preserveScroll: true,
});
Trade-offs and Constraints
- Component Keys: Persistent layouts can sometimes fail to update if they rely on props that change. You may need to use unique keys or watchers to trigger updates within a persistent component.
- Memory Leaks: Because persistent layouts never unmount, any event listeners or timers started within them must be cleaned up manually if they are conditionally toggled.
- Server-Side Logic: Partial visits require the server to be aware of which props are being requested to truly save database resources (e.g., using
Inertia::lazy()in Laravel).
Verification Checklist
- Layout Persistence: Place a counter in your Layout component. Navigate between two different pages. If the counter resets to zero, your layout is not persistent.
- Payload Size: Open Chrome DevTools > Network. Trigger a partial visit. Check the JSON response to ensure only the specified keys are present.
- Form State: Use the
useFormhook for inputs. Submit an empty form to verify that server-side validation errors are automatically mapped back to theerrorsprop without a full page reload.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.