Search code examples
next.jsnext.js13

Where do I configure my NextJS 13 project to use or not use the RootLayout?


Cliff's Notes:

I am in a basic, early-stage NextJS project and want to use the RootLayout, but changes to this file (including deleting it) have no effect on how my pages are rendered.

Details

I generated my NextJS 13 project as described in the NextJS docs, like this:

npx create-next-app@latest

This resulted in a file structure like this:

src
- app
  - favicon.ico
  - globals.css
  - layout.tsx
  - page.tsx
- pages
  - example-page.tsx

In src/app/layout.tsx, the RootLayout component is defined, including html and body tags, and the {children} are passed here to the body. It looks like this by default:

import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        {children}
      </body>
    </html>
  )
}

In several places, the NextJS documentation states clearly that the RootLayout component defined in app/layout.tsx will be the basis for all routing/pages in your app. Here is one example: https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts

The top-most layout is called the Root Layout. This required layout is shared across all pages in an application. Root layouts must contain html and body tags.

Doesn't get much more clear than that!

In practice I'm not seeing this RootLayout being used at all. I first encountered this issue when trying to apply app-level Context, like so:

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <MyContextProvider>
          {children}
        </MyContextProvider>
      </body>
    </html>
  )
}

I was encountering trouble with this approach, and in my investigation of why, discovered that even something as simple as a div was not being included in my example-page.tsx render. In the following code, I expected to find a div with the text content "Test div" rendered somewhere in the browser:

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        {/* This div does not render in the browser whatsoever */}
        <div>Test div</div>
        {children}
      </body>
    </html>
  )
}

But this was not the case. Just to see what would happen, I deleted layout.tsx completely, expecting to see the build or app fail is some way, and instead my app still works.

So my question is, why isn't my RootLayout being used, and how can I make my NextJS 13 app use it?

And incidentally, if the RootLayout isn't being used, and I haven't created any other custom layouts of my own, where is NextJS getting its base html and body template from to build/render these pages?


Solution

  • In NextJS 13.x, files defining pages go in app folder subdirectories and should be named page.tsx (or page.jsx as appropriate), e.g. src/app/login/page.tsx. In my folder structure from the original question you can see I was still using the NextJS 12.x file structure with a pages directory. The layout.tsx file will not pick up pages placed there. In NextJS 13.x, the old 12.x way of doing things is permitted in parallel with the new way. This is, in fact, alluded to in the documentation.