With Inertia, each page in your application has its own controller and corresponding JavaScript component. This allows you to retrieve just the data necessary for that page, no API required.
Pages are simply JavaScript components. There is nothing particularly special about them. Pages receive data from the controllers as props. Here's an example of a simple page component.
<template>
<layout title="Welcome">
<h1>Welcome</h1>
<p>Hello {{ user.name }}, welcome to your first Inertia app!</p>
</layout>
</template>
<script>
import Layout from './Layout'
export default {
components: {
Layout,
},
props: {
user: Object,
},
}
</script>
While not required, for most projects it makes sense to create a site layout that your pages can extend. Notice in our page example above that we're wrapping the page content within a <layout>
component. Here's an example of such a component. There is nothing Inertia specific here. This is just a standard JavaScript component.
<template>
<main>
<header>
<inertia-link href="/">Home</inertia-link>
<inertia-link href="/about">About</inertia-link>
<inertia-link href="/contact">Contact</inertia-link>
</header>
<article>
<slot />
</article>
</main>
</template>
<script>
export default {
props: {
title: String,
},
watch: {
title: {
immediate: true,
handler(title) {
document.title = title
},
},
},
}
</script>
While it's simple to implement layouts as children of the page components, it does force the layout instance to be destroyed and recreated between visits. This means you cannot have persistent layout state when navigating between pages.
For example, maybe you have an audio player on a podcast website that you want to continue playing as users navigate the site. Or, maybe you simply want to maintain the scroll position in your navigation between page visits. In these situations, using the persistent layouts feature in Inertia is a better choice.
<template>
<div>
<h1>Welcome</h1>
<p>Hello {{ user.name }}, welcome to your first Inertia app!</p>
</div>
</template>
<script>
import Layout from './Layout'
export default {
// Using a render function
layout: (h, page) => h(Layout, [page]),
// Using the shorthand
layout: Layout,
props: {
user: Object,
},
}
</script>
You can also create more complex layout arrangements using nested layouts.
<template>
<div>
<h1>Welcome</h1>
<p>Hello {{ user.name }}, welcome to your first Inertia app!</p>
</div>
</template>
<script>
import SiteLayout from './SiteLayout'
import NestedLayout from './NestedLayout'
export default {
// Using a render function
layout: (h, page) => {
return h(SiteLayout, [
h(NestedLayout, [page]),
])
},
// Using the shorthand
layout: [SiteLayout, NestedLayout],
props: {
user: Object,
},
}
</script>
When navigating between pages, Inertia will automatically reset the scroll position to the top of the body (unless disabled via the preserve-scroll
functionality). Depending on your app, you may have nested scroll containers within your layout, and these will not be reset by default. You can tell Inertia to also reset these areas by adding a scroll-region
attribute.
<div class="overflow-y-auto" scroll-region>
<!-- Your page content -->
</div>
While it's possible to pass title and meta tag props from pages to layouts (as illustrated above), it's often easier to manage this using a document head library like Vue Meta or React Helmet. Svelte has built-in support for manipulating the document head with the <svelte:head>
element.
<template>
<layout>
<h1>Welcome</h1>
<p>Hello {{ user.name }}, welcome to your first Inertia app!</p>
</layout>
</template>
<script>
import Layout from './Layout'
export default {
metaInfo() {
return {
title: `Welcome ${this.user.name}`,
}
},
components: {
Layout,
},
props: {
user: Object,
},
}
</script>
Further, if it's critical for your application to set the page title and meta tags server-side, you can use root template data to accomplish this.