Spring Sale 🍃 is live! Get 20% off ANY package with the coupon SPRINGBREAK

Persistent layouts in Inertia.js

When Inertia.js performs a visit, it will completely re-render the whole page, including the layout instance and components you might want to keep alive.

This behavior is intended and would work well in most cases. However, there are certain situations when you need to preserve the state of the layout’s components. The most common example is a media player that you want to keep playing as users navigate different website pages.

Please note that persistent layouts don't support slots. If you plan to use complex templates with custom slots, you should stick to the traditional approach.

Inertia.js supports persistent layouts that aren’t re-mounted during the visits. Persistent layouts are defined in the layout option of the frontend component.

<script>
import Layout from './Layout'
 
export default {
layout: Layout,
 
props: {
user: Object,
},
 
data() {
//
}
}
</script>

Script setup

Vue 3.3

Starting Vue 3.3, you can define a component‘s options using the new defineOptions macro right within the primary <script setup> block.

You can define a layout for a page using the macro.

<script setup>
import Layout from './Layout'
 
defineOptions({ layout: Layout })
</script>

Previous versions

Vue 3 and Vue 2.7 components with <script setup> syntax don’t let you define custom options. However, there’s still a workaround.

You can declare an additional <script> section in your vue components and define the layout. The bundler will merge both <script> parts during the build, preserving the layout option.

<script>
import Layout from './Layout'
export default {
layout: Layout,
}
</script>
<script setup>
// component's logic
</script>

Layout plugin

This plugin is only recommended to use with the older versions of Vue.js (2.7 – 3.2).

Alternatively, you can use the plugin defineLayout to define layouts directly within <script setup>.

Install the package momentum-layout.

npm i momentum-layout
# or
yarn add momentum-layout

Then, register the plugin by importing it and pushing it to the plugins array in vite.config.ts.

import inertiaLayout from "momentum-layout"
 
export default defineConfig({
plugins: [
inertiaLayout(),
],
})

defineLayout is a compile-time macro; it's not an actual method that needs to be imported. To make the TypeScript engine understand the macro, add momentum-layout/macro to the array types in tsconfig.json.

{
"compilerOptions": {
"types": [
"vite/client",
"momentum-layout/macro"
]
}
}

Import the layout component, and pass it to the defineLayout hook.

<script setup>
import Layout from "@/layouts/guest.vue"
 
defineLayout(Layout)
</script>
 
<template>
<H1>Welcome</H1>
<p>Hello {{ user.name }}, welcome to your first Inertia app!</p>
</template>

You can also create more complex layout arrangements using nested layouts.

<script setup>
import MainLayout from "@/layouts/main.vue"
import SettingsLayout from "@/layouts/settings.vue"
 
defineLayout([MainLayout, SettingsLayout])
</script>

Default layouts

If you broadly use persistent layouts across your app, you may find it handy to define the default layout in the resolve() callback of your application’s entry point.

import Layout from "../views/layouts/main.vue"
 
createInertiaApp({
resolve: (name: string) => {
const page = resolvePageComponent(
resolve: async (name: string) => {
const page = await resolvePageComponent(
`../views/pages/${name}.vue`,
import.meta.glob<DefineComponent>("../views/pages/**/*.vue")
)
 
page.default.layout = page.default.layout || Layout
 
return page
},
setup({ el, App, props, plugin }) {
...
},
})

This will set a default layout for every page if it has not been overridden on the component’s level.

You can define layouts conditionally for different parts of your website. For example, you usually have different layouts for authorization pages, the public section, and the admin panel.

createInertiaApp({
resolve: async (name: string) => {
const page = await resolvePageComponent(
`../views/pages/${name}.vue`,
import.meta.glob<DefineComponent>("../views/pages/**/*.vue")
)
 
const layout = name.startsWith('auth/') ? LayoutAuth : LayoutMain
 
page.default.layout = page.default.layout || layout
 
return page
},
setup({ el, App, props, plugin }) {
...
},
})

Use Inertia.js like a boss

Learn advanced concepts and make apps with Laravel and Inertia.js a breeze to build and maintain.

© 2023 Boris Lepikhin. All rights reserved.