Currying Layout Component Patterns in Next.js
Tags: javascript, nextjs, react, tutorial
Published: 2022-10-21 11:18:22

In Next.js, you can use the **Layout component pattern** by adding the `getLayout` property to the pages. This property is a function that returns a React component. The Layout component can be used to add *common UI elements* to all pages, such as navigation or footer.

Not only this pattern avoids duplicate code, but it can also persist state between page navigations. It's a great way to add a **Single-Page Application** (SPA) experience to your Next.js application.

You can find more details about this pattern in [the Next.js documentation](https://nextjs.org/docs/basic-features/layouts). But, the first time I saw this pattern was from Adam Wathan's blog post about [Persistent Layout Patterns in Next.js](https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/). He really gives a great explanation of this pattern and how to use it.

## The limitation of the Layout component pattern

The **Layout component pattern** is extremely useful when some parts of the UI elements are *exactly the same*. For example, the footer of the website: between each page, the footer doesn't change at all.

So, you can easily create a *function that returns the footer*. Then, you can use it as the Layout component in all Next.js pages. Here is a simple example of a Layout component that returns a footer:

```tsx
const getFooter = (page: ReactElement) => (
  <Footer>{page}</Footer>
);
```

In Next.js pages, you can use this function as the Layout component:

```tsx
// pages/index.js
export default function Page() {
  return {
    /** Your content */
  }
}

Page.getLayout = getFooter;
```

But for more complex applications, like the user/admin dashboard, the common UI elements can be *slightly different* between pages. I would love to *add a title to the header*, and for each page the title is different. Here is an example:

![Nextjs layout pattern](https://creativedesignsguru.com/assets/images/themes/next-js-layout-pattern.png)

![Next.js layout component](https://creativedesignsguru.com/assets/images/themes/nextjs-layout-component.png)

And, the only difference between the pages is the title. The rest of the UI elements are the same. So, how can I reuse the **Layout component** and still be able to change the title? You can create a new function for each different title, but it's not a good solution. Here is a potential example:

```tsx
const getDashboardChangeEmail = (page: ReactElement) => (
  <AuthProvider>
    <StateProvider>
      <Dashboard title="Change Email">{page}</Dashboard>
    </StateProvider>
  </AuthProvider>
);
```

```tsx
const getDashboardChangePassword = (page: ReactElement) => (
  <AuthProvider>
    <StateProvider>
      <Dashboard title="Change password">{page}</Dashboard>
    </StateProvider>
  </AuthProvider>
);
```

You have to create a new function for each different title. You have a lot of *duplicate code* and it's *hard to maintain*. And, if you want to add a new component, you need to update all of them.

::: newsletter

## Currying Layout component is the solution

Instead of duplicating code for each title, you can **curry the function**. Now, you can create a function that takes the `title` as a *parameter* and *return a function*. And the second function will return the React component:

```tsx
const getDashboard = (title: title) => (page: ReactElement) => (
  <AuthProvider>
    <StateProvider>
      <Dashboard title={title}>{page}</Dashboard>
    </StateProvider>
  </AuthProvider>
);
```

And, here is how you can use it on Next.js pages:

```tsx
// pages/change-email.js
export default function Page() {
  return {
    /** Your content */
  }
}

Page.getLayout = getDashboard('Change Email');
```

```tsx
// pages/change-password.js
export default function Page() {
  return {
    /** Your content */
  }
}

Page.getLayout = getDashboard('Change Password');
```

Now, we can reuse the same **Layout component** for all pages. And, we can still *change the title* for each page. The code isn't duplicated anymore. It also doesn't require a lot of changes to the existing code.

I'm totally open to *any feedback or suggestions*, please feel free to contact me on Twitter at [@ixartz](https://twitter.com/ixartz).