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# oryarn 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 || Layoutreturn 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 : LayoutMainpage.default.layout = page.default.layout || layoutreturn page},setup({ el, App, props, plugin }) {...},})