Creating Layouts and Defining Default Layouts

In this lesson, we'll learn how to create and define layouts within our AdonisJS and InertiaJS app. We'll also learn how we can easily add support for default layouts as well.

Published
Jul 18, 22
Duration
6m 15s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

With most applications, it makes sense to have at least one layout that we can use across all our pages to easily apply things like navbars, authentication states, and the like. They also help keep things consistent when traveling from page to page.

What Not To Do

Your first notion when applying a layout may be to just import and wrap each page with the layout component. Something like the below.

<template>
  <default-layout>
    <h1>My Home Page</h1>
  </default-layout>
</template>

<script>
import DefaultLayout from '../Layouts/Default.vue'

export default {
  components: {
    DefaultLayout
  }
}
</script> 

However, using this approach will result in our layout needing to be re-rendered repeatedly as our users switch between pages since the layout itself is a part of the page’s content. Not to mention, we need to manually apply the layout for each page. So, although this approach does work it’s less than optimal.

The InertiaJS Layout Property

InertiaJS actually supports layouts out of the box. When we define our page component, InertiaJS will check it for a layout property. If the layout property is a Vue component, that component will be used as the layout for the page and the page itself will be rendered within the layout’s <slot /> content.

Meaning, that we can alter our above example to the below; achieving the same results in a more performant manner because InertiaJS will only swap out the layout component when it’s changed.

<template>
  <div>
    <h1>My Home Page</h1>
  </div>
</template>

<script>
import DefaultLayout from '../Layouts/Default.vue'

export default {
  layout: DefaultLayout
}
</script> 

InertiaJS Layouts & Vue 3 Setup Scripts

You may be wondering what to do with pages where you’re using a Vue 3 setup script since those don’t have export default objects.

You may attempt to reach for defineExpose, since that can be used to expose properties outside of the component’s setup, but unfortunately that doesn’t work for this use case.

Instead, we’ll want to add a secondary script that does utilize export default and export the layout property that way.

<template>
  <div>
    <h1>My Home Page</h1>
    <div>{{ testing }}</div>
  </div>
</template>

<script>
import DefaultLayout from '../Layouts/Default.vue'

export default {
  layout: DefaultLayout
}
</script> 

<script setup>
import { ref } from 'vue'

const testing = ref('test')
</script>

It’s not pretty, but it works. Additionally, we'll discuss applying default layouts a little later in this lesson. This will allow us to bypass defining a layout on the vast majority of our pages, so this won’t be as big of an issue.

Defining A Layout

So, what does this DefaultLayout component actually look like? Well, it can be as complex, including nested layouts and render functions, or simple as you need. The main thing to keep in mind is that it will need to have a default slot so that the page's content can be included.

A typical application layout might include a navbar, alert and dialog helpers, and things of that nature. Below is a basic example.

<template>
  <main>
    <!-- simple navbar -->
    <header class="flex items-center justify-between">
      <div>Adonis + Inertia</div>
      <nav class="grid gap-3 grid-cols-3">
        <Link href="/page-1">Page 1</Link>
        <Link href="/page-2">Page 2</Link>
        <Link href="/page-3">Page 3</Link>
      </nav>
      <div class="flex justify-end">
        <Link href="/login">Login</Link>
        <Link href="/register">Register</Link>
      </div>
    </header>

    <!-- page content -->
    <slot />
  </main>
</template>

<script setup>
  // resources/js/Layouts/Default.vue
</script>

In some cases, you may need a second layout. For example, if the user needs to be authenticated to get into the application, then it’s likely the default layout is going to utilize some user information. However, on our authentication pages, that’s not needed.

So, we can create a second layout specifically for these pages.

<template>
  <div class="flex flex-col items-center justify-center w-full h-full max-w-sm mx-auto pt-12">
    <h1 class="text-center">
      Adonis + Inertia
    </h1>
    
    <div class="w-full bg-white p-6 rounded-xl shadow-xl">
      <slot />
    </div>
  </div>
</template>

<script setup>
  // resources/js/Layouts/Auth.vue
</script>

<style>
  body {
    background: theme('colors.gray.100');
  }
</style>

Setting An Inertia Default Layout

So far, we’ve been defining all our layouts within our page components. This is beneficial when you have more complex layouts. However, we can set up our pages to where anytime we don’t specify a particular layout to use, it’ll use our default layout.

To do this, we’ll need to slightly alter the resolve method within our app.js file. Remember, this method is used to grab the page component from our project and return it to Inertia.

// resources/js/app.js

import '../css/app.css'
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
import DefaultLayout from './Layouts/Default.vue' // 👈 1. import the default layout

createInertiaApp({
  resolve: name => {
    // 2. get the page component
    const page = require(`./Pages/${name}`).default

    // 3. set the default layout, if a layout isn't defined
    if (!page.layout) {
      page.layout = DefaultLayout
    }
    
    // 4. return the page
    return page
  },
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})

All we need to do is import our DefaultLayout, check to see if the imported page component is defining a layout. If it’s not, we’ll define our DefaultLayout. Then, we’ll return the page for InertiaJS to use.

Using The Default Layout

Now, in order to make use of this, all we need to do is completely remove anything layout-based within our pages.

<template>
  <div>
    <h1>My Home Page</h1>
    <div>{{ testing }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const testing = ref('test')
</script>

This means that we’ll no longer need to use the second script when using a setup script.

Using A Secondary Layout

When it comes to using a secondary layout, like our AuthLayout, instead of the default layout, all we need to do is import and define the layout property as we were before.

<template>
  <div>
    <h1>Login</h1>
  </div>
</template>

<script>
import AuthLayout from '../Layouts/Auth.vue'

export default {
  layout: AuthLayout
}
</script> 

Now our AuthLayout will be used for our login page instead of the DefaultLayout.

Join The Discussion! (2 Comments)

Please sign in or sign up for free to join in on the dicussion.

  1. Commented 1 month ago

    Upvote for a V6 version of this tutorial (now that the inertia package is supported by the Adonis team)

    1

    Please sign in or sign up for free to reply

    1. Commented 1 month ago

      Hey Eric! An updated version of this series is on the way, being planned now, and will begin shortly after we finish our Let's Learn AdonisJS 6 series :)

      1

      Please sign in or sign up for free to reply