Building with AdonisJS & Inertia #2.7

Default Layouts & Overwriting the Default Layout

In This Lesson

We'll inspect how Inertia injects our layout component and the data passed to it. We'll also learn how we can overwrite our default layout from our page components.

Created by
@tomgobich
Published

⏳ Chapters

00:00 - Inspecting Our Layout's Prop Data
00:36 - Checking If Layout Is Already Set
02:24 - Creating Our AppLayout
02:46 - Using AppLayout As Our Default Layout
03:25 - Using A Different Layout from the Default

Join the Discussion 7 comments

Create a free account to join in on the discussion
  1. Hey Tom,

    I was following along and got really stuck on this specific section, due to it being in React vs Vue:

    For anyone who gets stuck adding different layouts from the default, you won't be able to follow it step by step using React. Instead, do this:

    (PART 1)
    export default function Login() { /* The Login page*/}
    Login.layout = (page: ReactElement) => <AuthLayout children={page} />

    Source: https://inertiajs.com/pages

    3
    1. Responding to vladimir-laskin

      (PART 2)

      In your SSR/APP config, you would need to do something along the following:

          resolve: (name) => {

            const pages = import.meta.glob('../pages/**/*.tsx', { eager: true })

            const resolvedPage = pages[`../pages/${name}.tsx`] as {

              default: ComponentType & { layout?: (page: ReactElement) => ReactElement }

            }

            if (!resolvedPage.default.layout) {

              resolvedPage.default.layout = (page) => <AppLayout>{page}</AppLayout>

            }

            return resolvedPage

          },

      2
      1. Responding to vladimir-laskin

        (PART 3)

        What the code above basically tells Inertia is this:

        1 - Get the whole page itself
        2 - Check if it is missing a layout.
        3 - If it's missing a layout, wrap it around <AppLayout>, and go to step 4. (Skip if it's not missing a layout)
        4 - Return the page for rendering

        It keeps all the benefits of not GETing the resources all over again, and is very intuitive since you could add nest your layouts further like so:

        https://inertiajs.com/pages#persistent-layouts (2nd example)

        2
        1. Responding to vladimir-laskin
          @tomgobich

          Thanks so much for sharing, Vladimir!!

          1
        2. Responding to vladimir-laskin
          @juan-agu

          this was so helpful!

          1
  2. @gregory

    I spent the afternoon making it work with react.

    @inertiajs/react has a type called ResolvedComponent

    (alias) type ResolvedComponent = ComponentType<any> & {
        layout?: LayoutComponent | LayoutComponent[] | LayoutFunction;
    }
    import ResolvedComponent
    Copied!

    I imported ResolvedComponent and used it to type import.meta.glob in app.tsx and ssr.tsx. I then followed the code provided in the Inertia documentation to add a fallback to a default layout.

    /// <reference path="../../adonisrc.ts" />
    /// <reference path="../../config/inertia.ts" />
    
    import '../css/app.css'
    import { hydrateRoot } from 'react-dom/client'
    import { createInertiaApp, type ResolvedComponent } from '@inertiajs/react'
    import { resolvePageComponent } from '@adonisjs/inertia/helpers'
    import { ReactNode } from 'react'
    import GuestLayout from '~/layouts/GuestLayout'
    
    const appName = import.meta.env.VITE_APP_NAME || 'AdonisJS'
    
    createInertiaApp({
      progress: { color: '#5468FF' },
    
      title: (title) => `${title} - ${appName}`,
    
      resolve: async (name) => {
        const page = await resolvePageComponent(
          `../pages/${name}.tsx`,
          import.meta.glob<{ default: ResolvedComponent }>('../pages/**/*.tsx')
        )
    
        page.default.layout =
          page.default.layout || ((page: ReactNode) => <GuestLayout children={page} />)
    
        return page
      },
    
      setup({ el, App, props }) {
        hydrateRoot(el, <App {...props} />)
      },
    })
    
    Copied!
    import ReactDOMServer from 'react-dom/server'
    import { createInertiaApp, type ResolvedComponent } from '@inertiajs/react'
    import GuestLayout from '~/layouts/GuestLayout'
    import { ReactNode } from 'react'
    
    export default function render(page: any) {
      return createInertiaApp({
        page,
        render: ReactDOMServer.renderToString,
        resolve: (name) => {
          const pages = import.meta.glob<{ default: ResolvedComponent }>('../pages/**/*.tsx', {
            eager: true,
          })
          const page = pages[`../pages/${name}.tsx`]
    
          page.default.layout =
            page.default.layout || ((page: ReactNode) => <GuestLayout children={page} />)
    
          return page
        },
        setup: ({ App, props }) => <App {...props} />,
      })
    }
    Copied!

    You can apply a layout of your choice to your pages like this:

    import { ReactNode } from 'react'
    import { RegisterForm } from '~/components/register-form'
    import AuthLayout from '~/layouts/AuthLayout'
    
    export default function Register() {
      return (
        <div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
          <div className="w-full max-w-sm">
            <RegisterForm />
          </div>
        </div>
      )
    }
    
    Register.layout = (page: ReactNode) => <AuthLayout children={page} />
    Copied!

    1
    1. Responding to gregory
      @tomgobich

      Thank you for sharing, @gregory!! I'm happy to hear you were able to get it up and working with React!

      0