A guide to Next.js layouts and nested layouts
Tags: nextjs, webdev, frontend
Published: 2023-07-06 16:41:50

**Written by [Ibadehin Mojeed](https://blog.logrocket.com/author/ibadehinmojeed/)✏️**

When building projects with Next.js, we typically create an entire user interface by assembling isolated components. 

However, some parts of the interface require the same code snippets across multiple routes — for example, the navigation header, footer, and sidebar. To manage this, we use layouts to structure the interface in a way that contains shared code snippets. 

In this lesson, we'll delve into managing layouts and nested layouts in Next.js using the Pages Router and the App Router. We will cover:

*   [Managing layouts with the Pages Router](#managing-layouts-next-js-pages-router)
    *   [Persisting layouts in the Pages Router](#persisting-layouts-next-js-pages-router)
*   [Creating nested layouts with the Pages Router](#creating-nested-layouts-next-js-pages-router)
    *   [Composing per-page layouts with the `getLayout` function](#composing-per-page-layouts-getlayout-function)
    *   [Creating a nested `DashboardLayout`](#creating-nested-dashboardlayout)
*   [Creating a custom layout in the Pages Router](#creating-custom-layout-next-js-pages-router)
*   [Managing layouts in the App Router](#managing-layouts-next-js-app-router)
*   [Creating nested layouts in the App Route](#creating-nested-layouts-next-js-app-router)
*   [Creating a custom layout in the App Router](#creating-custom-layout-next-js-app-router)
    *   [Creating route groups](#creating-route-groups)

Check out [the demo project](https://nextjs-nested-layout.vercel.app/) I've put together to see it in action: ![Next Js Nested Layouts Demo Project Showing Vercel Webpage With Three Menu Navigation Items, A Search Bar, And Various Page Elements](http://blog.logrocket.com/wp-content/uploads/2023/06/img1-Next-js-nested-layouts-demo-project.png) You can see the [project source code](https://github.com/Ibaslogic/nextjs-nested-layout-app-dir) on GitHub as well. Here are the layout details for the project:

*   The `Home` and `/dashboard/*` routes share header and footer content
*   The `Newsletter` route has a different footer content and no header
*   The `/dashboard/*` routes implement a nested layout sharing sidebar navigation

To follow along with this tutorial, you’ll need a basic understanding of Next.js. Let’s get started!

## Managing layouts with the Next.js Pages Router <a name="managing-layouts-next-js-pages-router">

Next.js recommends starting a new project with the App Router. However, in this tutorial, we'll also discuss how to implement layouts and nested layouts with the Pages Router for users who have yet to migrate to the [new Next.js routing system](https://blog.logrocket.com/next-js-13-app-directory/#page-directory-vs-app-directory). 

To help illustrate the differences between the two approaches, we'll create the same application using both methods and compare how the new App Router simplifies the process of implementing nested layouts. 

To start, let's take a look at a typical folder structure for the Pages Router in Next.js:

```bash
...
├── components
│    ├── Footer.js
│    └── Header.js
├── pages
│    ├── dashboard
│    │       ├── account.js
│    │       ├── analytics.js
│    │       └── settings.js
│   ...
│    ├── index.js
│    └── newsletter.js
...
```

To define a layout with the Pages routing system, we create a `Layout` component that renders any shared user interface and its children. 

Create a `components/Layout.js` file and render the shared header and footer content:

```javascript
import Header from './Header';
import Footer from './Footer';

const RootLayout = ({ children }) => {
  return (
    <>
      <Header />
      <main>{children}</main>
      <Footer />
    </>
  );
};

export default RootLayout;
```

The `Layout` component takes a `children` prop that serves as a placeholder for the active page content. 

In the final project, we use [Tailwind CSS for styling purposes](https://blog.logrocket.com/how-to-use-tailwind-css-react-vue-js/). As a result, the updated markup includes class utilities:

```javascript
const RootLayout = ({ children }) => {
  return (
    <div className="flex flex-col min-h-screen mx-auto max-w-2xl px-4 pt-8 pb-16">
      <div className="flex-grow">
        <Header />
        <main className="my-0 py-16">{children}</main>
      </div>
      <Footer />
    </div>
  );
};
```

A beginner’s approach might be to wrap each page's rendered markup with the `<RootLayout>` component. For example, wrapping the `Home` component render would look like this:

```javascript
import RootLayout from '@/components/Layout';

const Home = () => {
  return (
    <RootLayout>
      <main>{/* ... */}</main>
    </RootLayout>
  );
};
export default Home;
```

By doing this, we'll get the desired UI for the pages: ![User Shown Typing Into Search Bar In Next Js Nested Layouts Demo Project. Text In Search Bar Disappears When User Navigates To A Different Page](http://blog.logrocket.com/wp-content/uploads/2023/06/img2-Next-js-nested-layouts-demo-project-UI.gif) However, this implementation doesn't [preserve the state between page transitions](https://blog.logrocket.com/guide-state-management-next-js/). For example, the search field's input text gets cleared when navigating between pages that share a common layout. This isn't the experience we expect from a single-page application. 

In the next section, we'll discuss how to preserve the state in a shared layout.

### Persisting layouts in the Next.js Pages Router <a name="persisting-layouts-next-js-pages-router">

If we examine the `pages/_app.js` file that Next.js calls during each page initialization, we’ll see an `App` component that includes a `Component` prop representing the active page:

```javascript
import '@/styles/globals.css'

export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />
}
```

In this file, we can load the shared layout and other global files, such as the global CSS file. So, let’s wrap the page content with the `<RootLayout>` like so:

```javascript
import '@/styles/globals.css';
import RootLayout from '@/components/Layout';

export default function App({ Component, pageProps }) {
  return (
    <RootLayout>
      <Component {...pageProps} />
    </RootLayout>
  );
}
```

With this implementation, the `RootLayout` component is reused between page transitions. As a result, the state in the shared component, such as the `Header`, will be preserved. 

We no longer need to wrap each page's render with the `<RootLayout>` component. 

After saving all files and revisiting the application, we can write in the search field and see that the state now persists between page changes, which is an improvement! 

Note that you may need to restart the development server if it doesn't work as expected.

## Creating nested layouts with the Next.js Pages Router <a name="creating-nested-layouts-next-js-pages-router">

To create a nested shared layout, as demonstrated in the `/dashboard/*` pages, we need to nest a new layout that renders the sidebar navigation within the root layout. 

However, with the current implementation, simply wrapping the active route within the root layout — as we did in the `pages/_app.js` file — only works if we require one layout for the entire application. 

To achieve a nested layout, Next.js provides a way to compose layouts on a per-page basis.

### Composing per-page layouts with the `getLayout` function <a name="composing-per-page-layouts-getlayout-function">

With the Next.js pages directory, we can create multiple layouts and nest them, or create a custom layout that applies to specific routes using a per-page implementation. That means, instead of rendering a root layout in the `pages/_app.js` file, we'll let each individual page component be in charge of its entire layout. 

Let's begin with the `Home` page. We can achieve a per-page layout by applying a `getLayout` property on the page component:

```javascript
import RootLayout from '@/components/Layout';

const Home = () => {
  return <main>{/* ... */}</main>;
};

Home.getLayout = (page) => {
  return <RootLayout>{page}</RootLayout>;
};

export default Home;
```

We defined a function that takes the current page as a parameter and returns the desired UI for the index page. Note that we don’t have to use the name `getLayout` — it can be any name. 

We can now invoke that function in the `pages/_app.js` file, and pass the current page as an argument. To achieve this, we'll modify the `App` component in the `pages/_app.js` as follows:

```javascript
export default function App({ Component, pageProps }) {
  // If page layout is available, use it. Else return the page
  const getLayout = Component.getLayout || ((page) => page);
  return getLayout(<Component {...pageProps} />);
}
```

When Next.js initializes a page, it checks if a per-page layout is defined in the page component using the `getLayout` function. If the layout is defined, it is used to render the page. Otherwise, the page is rendered as is. 

After saving the file, the `Home` page should now render with the specified layout.

### Creating a nested `DashboardLayout` <a name="creating-nested-dashboardlayout">

To create a nested layout for pages under the `/dashboard/*` route segments, we need to create a new layout file called `components/DashboardLayout.js`. This file should export a component that returns a shared UI for these pages and uses the `children` prop to render their respective content:

```javascript
const DashboardLayout = ({ children }) => {
  return (
    <div className="flex gap-8">
      <aside className="flex-[2]">
        {/* Include shared UI here e.g. a sidebar */}
      </aside>
      <div className="bg-gray-100 flex-[8] p-4 rounded min-h-[300px]">
        {children}
      </div>
    </div>
  );
};

export default DashboardLayout;
```

Now, in each of the `/dashboard/*` page files, we need to apply a `getLayout` property on the page component and return the desired layout tree. 

For example, the `/dashboard/account.js` file will look like this:

```javascript
import RootLayout from '@/components/Layout';
import DashboardLayout from '@/components/DashboardLayout';

const Account = () => {
  return <div>Account screen</div>;
};

Account.getLayout = (page) => (
  <RootLayout>
    <DashboardLayout>{page}</DashboardLayout>
  </RootLayout>
);

export default Account;
```

Notice how the `DashboardLayout` is nested within the `RootLayout`. 

If we apply the `getLayout` property to the other page components under the `/dashboard/*` route, we’ll also get the desired layout where the state persists between page transitions: ![User Shown Typing Into Search Bar In Next Js Nested Layouts Demo Project. Text In Search Bar Remains When User Navigates To A Different Page](http://blog.logrocket.com/wp-content/uploads/2023/06/img3-Next-js-nested-layouts-demo-project-UI-state-persists-page-transitions.gif) Check out the [GitHub files for the other page components](https://github.com/Ibaslogic/nextjs-nested-layout-pages-dir/blob/main/pages/dashboard/settings.js) to double-check your work so far.

## Creating a custom layout in the Next.js Pages Router <a name="creating-custom-layout-next-js-pages-router">

You may want to create a custom layout as we have done on the `Newsletter` page in the final project. That layout renders different footer content, with no navigation bar or sidebar. 

We'll create a new layout file `components/OnlyFooterLayout.js` and returns the custom footer and `children` prop. The code for this component would look like this:

```javascript
import NewsletterFooter from './NewsletterFooter';

const OnlyFooterLayout = ({ children }) => {
  return (
    <div className="flex flex-col min-h-screen mx-auto max-w-2xl px-4 pt-8 pb-16">
      <div className="flex-grow">
        <main className="my-0 py-16">{children}</main>
      </div>
      <NewsletterFooter />
    </div>
  );
};

export default OnlyFooterLayout;
```

Next, we’ll create the `components/NewsletterFooter.js` file and render some [custom footer content](https://github.com/Ibaslogic/nextjs-nested-layout-pages-dir/blob/main/components/NewsletterFooter.js):

```javascript
const NewsletterFooter = () => {
  return (
    <footer className="flex items-center justify-between">
      {/* ... */}
    </footer>
  );
};

export default NewsletterFooter;
```

Finally, in the `pages/newsletter.js` file, we’ll apply a `getLayout` property on the page component, then return the desired UI for the `Newsletter` page:

```javascript
import OnlyFooterLayout from '@/components/OnlyFooterLayout';

export const Newsletter = () => {
  return (
    // ...
  );
};

Newsletter.getLayout = (page) => (
  <OnlyFooterLayout>{page}</OnlyFooterLayout>
);

export default Newsletter;
```

If we save all files, the page should now render with the custom layout. 

See the [project source code](https://github.com/Ibaslogic/nextjs-nested-layout-pages-dir).

## Managing layouts in the Next.js App Router <a name="managing-layouts-next-js-app-router">

Next.js 13 introduced the App Router file system, which enables first-class support for layouts, nested routes, and nested layouts. Beginning with version 13.4, we are safe to use this new routing system in a production environment. 

In light of our project routes, the `app` directory structure would look something like this:

```bash
...
├── app
│   ...
│    ├── dashboard
│    │       ├── account
│    │       │     └── page.js
│    │       ├── analytics
│    │       │     └── page.js
│    │       └── settings
│    │             └── page.js
│    ├── newsletter  
│    │       └── page.js
│    ├── layout.js
│    └── page.js    
├── components
│   ...
│   
...
```

Each folder or nested folder in the `app` directory defines a route or nested route and requires a special `page.js` file to render its respective UI. 

The `layout.js` file at the top level is the root layout that is shared across all pages in the application. This file is required and would have the following structure by default:

```javascript
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

Next.js uses this root layout to wrap the page content or any nested layouts that may be present during rendering. 

Similar to what we did in the `pages` directory, we can also include the top-level shared components within this root layout like so:

```javascript
import Header from '@/components/Header';
import Footer from '@/components/Footer';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <div className="flex flex-col min-h-screen mx-auto max-w-2xl px-4 pt-8 pb-16">
          <div className="flex-grow">
            <Header />
            <main className="my-0 py-16">{children}</main>
          </div>
          <Footer />
        </div>
      </body>
    </html>
  );
}
```

This layout will persist across routes and maintain component state, as anticipated. 

It is important to note that the `app` directory represents the root segment, so the `app/page.js` file will render the UI of the index page:

```javascript
const Home = () => {
  return <main>{/* ... */}</main>;
};
export default Home;
```

It's worth noting that components in the `app` directory are [React Server Components](https://blog.logrocket.com/what-you-need-to-know-about-react-server-components/) by default, unlike those in the pages directory. As a result, we cannot use client-side hooks in these components. 

Therefore, to address this issue, we have extracted the logic for the active menu class and the search functionality — which utilize the `usePathname` and `useState` client-side Hooks, respectively — from the `Header` component and placed them in their [separate client components](https://github.com/Ibaslogic/nextjs-nested-layout-app-dir/blob/main/components/HeaderNavLink.js).

## Creating nested layouts in the Next.js App Router <a name="creating-nested-layouts-next-js-app-router">

To create a nested shared layout, specifically for pages under the `/dashboard/*` route segments, all we need to do is add a `layout.js` file inside the `dashboard` folder to define the UI:

```javascript
const DashboardLayout = ({ children }) => {
  return (
    <div className="flex gap-8">
      <aside className="flex-[2]">
        {/* Include shared UI here e.g. a sidebar */}
      </aside>
      <div className="bg-gray-100 flex-[8] p-4 rounded min-h-[300px]">
        {children}
      </div>
    </div>
  );
};

export default DashboardLayout;
```

This layout will be nested within the root layout and will wrap all the pages in the `/dashboard/*` route segment, providing a more specific and targeted UI for those pages. 

That’s all. As we can see, creating a nested layout in the App Router is incredibly easy and intuitive.

## Creating a custom layout in the Next.js App Router <a name="creating-custom-layout-next-js-app-router">

In order to design a personalized layout for the `Newsletter` page, we must isolate that specific route segment from the shared layouts. To accomplish this, we will employ route groups.

### Creating route groups <a name="creating-route-groups">

Route groups are a way to group related routes. For our project, we’ll create two route groups:

1.  A `custom` route group containing the `Newsletter` route: The layout for this group will render a custom layout with no navigation bar or sidebar and a different footer
2.  A `primary` route group containing both the index route and the `/dashboard/*` routes, since they share the same root layout

Note that we can name the route group anything we want. It's only for organizational purposes. 

To create a route group, we'll wrap the group name in parentheses. If we reorganize the `app` directory into two groups, we'll have the following structure:

```bash
...
├── app
│    ├── (primary)
│    │       ├── dashboard
│    │       │       ├── account
│    │       │       │     └── page.js
│    │       │       ├── analytics
│    │       │       │     └── page.js
│    │       │       ├── settings
│    │       │       │     └── page.js
│    │       │       └── layout.js         
│    │       ├── layout.js         
│    │       └── page.js          
│    ├── (custom)  
│    │       ├── newsletter
│    │       │        └── page.js
│    │       └── layout.js
│    └── layout.js    
│   ...
│   
...
```

Each of the groups has its respective layout, allowing us to customize the UI as desired. The `(primary)/layout.js` now looks like so:

```javascript
export default function MainLayout({ children }) {
  return (
    <div className="flex flex-col min-h-screen mx-auto max-w-2xl px-4 pt-8 pb-16">
      <div className="flex-grow">
        <Header />
        <main className="my-0 py-16">{children}</main>
      </div>
      <Footer />
    </div>
  );
}
```

Meanwhile, the `(custom)/layout.js` looks like so:

```javascript
import NewsletterFooter from '@/components/NewsletterFooter';

export default function CustomLayout({ children }) {
  return (
    <div className="flex flex-col min-h-screen mx-auto max-w-2xl px-4 pt-8 pb-16">
      <div className="flex-grow">
        <main className="my-0 py-16">{children}</main>
      </div>
      <NewsletterFooter />
    </div>
  );
}
```

Finally, the top-level `app/layout.js` file should now include the `<html>` and `<body>` tags as follows:

```javascript
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

If we save all the files, each route should render as expected! See the full [project source code](https://github.com/Ibaslogic/nextjs-nested-layout-app-dir) on GitHub.

## Conclusion

Understanding how layouts work in Next.js is crucial for building complex projects with the framework. In this guide, we've covered all the necessary steps to structure the rendered UI with shared content and use it across multiple routes. 

We've discussed how to achieve layouts and nested layouts both in the Pages Router and the new App Router. Additionally, we've seen how to use route groups to create custom layouts for specific route segments. 

If you found this guide helpful, we encourage you to share it with others. If you have any questions or contributions, feel free to leave a comment. 

[See the final project](https://nextjs-nested-layout.vercel.app/) hosted on Vercel.