How to Keep Layouts Alive with Inertia.js Persistent Layouts – A Practical Guide
Discover how Inertia.js persistent layouts keep shared UI components alive across page navigations, improve performance, and maintain state. Learn step‑by‑step implementation, verification, and trade‑offs in this practical guide.
25 Oct 2025, 05:08 UTC

Problem: Layouts Re‑Mount on Every Navigation
When you build a single‑page‑application (SPA) with Inertia.js, each inertia.visit loads a new page component. By default the layout wrapper – the header, sidebar, or any shared UI – is also re‑mounted. That means the browser has to re‑run the layout’s JavaScript, re‑apply CSS, and lose any internal state (e.g., which menu item is highlighted or a scroll offset). The result is a perceptible flicker and a slower feel for the user.
Thesis: Use Inertia’s Persistent Layout Feature
Inertia.js provides a built‑in way to keep a layout component mounted across page navigations. When the layout is persistent, Inertia only swaps the page component, leaving the layout untouched. This reduces bundle size, speeds up perceived load times, and preserves UI state.
1. What Is a Persistent Layout?
Inertia treats a layout as a higher‑order component that receives the page’s props. When a page component exports a layout property or a layout function, Inertia will render that layout once and reuse it for subsequent pages that declare the same layout. The layout’s mounted hook runs only on the first navigation; subsequent navigations skip the mounting phase.
2. Implementing a Persistent Layout in React
Below is a minimal example that shows how to create a layout that remembers which navigation link is active. The layout is defined once, exported from each page, and receives the page’s props.
// src/components/SidebarLayout.jsx
import { useEffect, useState } from 'react';
export default function SidebarLayout({ children, page }) {
const [active, setActive] = useState(page.props.activeLink ?? 'home');
// Reset active link when the page changes but keep the layout alive
useEffect(() => {
setActive(page.props.activeLink ?? 'home');
}, [page.props.activeLink]);
return (
- Home
- About
{children}
);
}
// src/pages/Home.jsx
import { Inertia } from '@inertiajs/inertia';
import SidebarLayout from '@/components/SidebarLayout';
function HomePage() {
return Welcome to the Home Page;
}
HomePage.layout = SidebarLayout;
HomePage.layoutProps = { activeLink: 'home' };
export default HomePage;
// src/pages/About.jsx
import SidebarLayout from '@/components/SidebarLayout';
function AboutPage() {
return About Us;
}
AboutPage.layout = SidebarLayout;
AboutPage.layoutProps = { activeLink: 'about' };
export default AboutPage;
Key points:
- Export a
layoutproperty from each page component. - Pass any page‑specific props via
layoutProps(or include them inpage.props). - The layout receives the full page object, so it can react to prop changes without remounting.
3. Verifying Persistence
To confirm that the layout stays mounted:
- Console check: Add
console.log('SidebarLayout mounted');inside the layout component. The message should appear only once, even after navigating between pages. - React DevTools: Open the component tree and verify that
SidebarLayoutremains in the hierarchy after each navigation. - Network tab: The JavaScript bundle for
SidebarLayoutshould load only on the first page load. Subsequent page component bundles should load separately. - Unit test (optional): Render two pages that share the same layout and assert that the layout’s internal counter increments only once. Example (pseudo‑code):
// jest test snippet
import { render } from '@testing-library/react';
import HomePage from '@/pages/Home';
import AboutPage from '@/pages/About';
test('layout mounts only once', () => {
const { container: homeContainer } = render();
const { container: aboutContainer } = render();
expect(homeContainer.querySelector('.app-container')).toBeTruthy();
// The layout should still be the same instance – use a ref or global counter
});
4. Trade‑offs and Limitations
Persistent layouts are powerful, but they come with caveats:
- Stale Props: If a page component changes its props but the layout does not reset state, the layout may display outdated data. Use
useEffectwith the relevant props as dependencies to sync state. - Server‑Side Rendering (SSR) Mismatch: When the layout uses client‑only data (e.g.,
windowor cookies), the server render will differ from the client mount, causing hydration warnings. Ensure SSR data matches or guard client‑only logic. - Over‑use: Not every component should be persistent. Forms or search bars that should reset on each visit are better off not being wrapped in a persistent layout.
- Memory Footprint: Keeping a layout alive means its state lives in memory for the life of the app. If the layout holds large data structures, consider clearing them when no longer needed.
Actionable Takeaway
To improve perceived performance and keep shared UI state intact:
- Define a reusable layout component and export it via
layoutfrom each page. - Pass page‑specific props through
layoutPropsorpage.propsand sync state withuseEffect. - Verify persistence using console logs, DevTools, and network analysis.
- Guard against stale data and SSR mismatches by resetting state when necessary.
- Use persistent layouts selectively – for navigation bars, footers, or sidebars that should remain constant.
Implementing persistent layouts is a straightforward way to make Inertia.js apps feel faster and more responsive while keeping the architecture clean.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.