🎁 Checkout my Learn React by Making a Game course and get 1 month Free on Skillshare!

NextJs 13 layouts tutorial: the layout.js file, nested layouts, and state preservation

Layouts were added before NextJs 13. However, starting from this version layouts have become a mandatory part of our NextJs applications.

What we will see in this tutorial:

  • how to use the basic app root layout
  • using nested layouts
  • how layouts preserve states

Before we start, if needed, be sure to take a look at how to setup your first NexJs 13 app.

How to use the app root layout

We will start with a simple app that has just a single index page with the following content:

// app/page.js
export default function Index() {
    return (<div>
        <h1>The red homepage</h1>
  </div>)
}

Alongside this index page we will add a layout.js file in the same /app folder, with the following content:

// app/layout.js
export default function RootLayout({ children }) {
 return (
    <html>
        <head></head>
        <body style={{"border":"10px solid red"}}>
            {children}
        </body>
    </html>
 )
}

Now, if we run the app we will see the below screen:

As the name implies layouts will define the wrapping for our main content. The actual content of the page comes from page.js while layout.js provides the red rectangle decoration.

Some notes on the layout.js page:

  • layout.js is a reserved filename. By adding a layout.js file next to a page.js we will get an automatic relation between them - the layout wrapping the content of the page
  • it is mandatory for our app to have a root app/layout.js. Even if we don’t manually make the file, the app/layout.js will be generated by NextJs at the first run. It kind of serves as a replacement for the _document page and the _app page
  • if you add a manually app/layout.js it’s mandatory to return the html and body tags
  • any layout that you create should accept a children prop which will serve as the placeholder for the page content (or another nested layout, a loading UI or an error UI)

Being shared by all pages we can use layouts as the place to import common things such as Font Awesome icons.

Using nested layouts

Alongside the root layout, we can also define layouts for each individual page.

The process is the same as for the root layout. We need to add a layout.js file in the same folder as the page.js.

For example, we will add an app/blue folder containing a layout.js and a page.js:

// app/blue/layout.js
export default function BlueLayout({ children }) {
    return (
        <div style={{"border":"10px solid blue"}}>
            {children}
        </div>
  )
}
// app/blue/page.js
export default function BluePage() {
    return (<h1>The blue page</h1>)
}

And now, if we visit http://localhost:3000/blue we will see the below page:

And we will do the same for a green page:

For both our blue and green pages we get the individual page content (eq: app/blue/page.js) wrapped in the individual page layout (eq: app/blue/layout.js). And all of this is wrapped in the root app layout (app/layout.js). These are the nested layouts in NextJs 13.

By the way, I've wrote an article also about how to make shared layouts in NextJs by using route groups.

Preserving layouts state in NextJs 13

Let's say we want to add some links so that we can easily navigate from one page to another.

The app/layout.js is a great place to add the navigation. Before NextJs 13 we were using the _app file to do this.

// app/layout.js
import Link from 'next/link'

export default function RootLayout({ children }) {
     return (
         <html>
             <head></head>
             <body style={{"border":"10px solid red"}}>
                 <nav>
                     <Link href="/">Home</Link>{' '}
                     <Link href="/green">Green</Link>{' '}
                     <Link href="/blue">Blue</Link> 
                 </nav>
                 {children}
             </body>
         </html>
     )
}

At this moment the main page will look like this:

If we navigate via these links components only the new parts of the page will be rendered. The root layout remains the same and only the nested layouts change.

Alongside the link prefetching this is great because it means that also the state is preserved. Let's add a randomly generated number in the root layout:

import Link from 'next/link'

export default function RootLayout({ children }) {
    // this is just a test value
    // don't do this in prod !!!
    const rand = Math.floor(Math.random() * 100)
    return (
        <html>
            <head></head>
            <body style={{"border":"10px solid red"}}>
                <h4>Random number {rand}</h4>
                // rest of the code here
            </body>
        </html>
    )
}

This number will not change when we navigate to any internal page. Keep in mind that this is true only if we navigate via the internal link components. If you do a full page refresh the state is reinstantiated and the random number is regenerated.

So, with this behavior, we can use the root layout for presenting things like user authentification, shopping chart icons, and more. Before this, we need to use an alternative like the React Context Api.

If you don't want to have the state preserved you can use NextJs templates.

And this concludes our introduction to the NextJs 13 layouts. You can download the full code from here.

Looking forward to seeing you at the next adventure in the NextJs realm!

📖 Frontend Odyssey: 25 projects in 100 days

Learn how to build production-ready web applications. Dive in with 25 projects with project briefs and wireframes!

  • ⭐️ 25+ React & JS projects
  • ⭐️ 100+ Interview questions
  • ⭐️ ChatGPT for developers
  • ⭐ 50+ Project Ideas

📖 Frontend Odyssey: 25 projects in 100 days

Learn how to build production-ready web applications. Dive in with 25 projects with project briefs and wireframes!

  • ⭐️ 25+ React & JS projects
  • ⭐️ 100+ Interview questions
  • ⭐️ ChatGPT for developers
  • ⭐ 50+ Project Ideas

One Reply

Leave a Reply

Your email address will not be published. Required fields are marked *