DEV Community

Cover image for Currying Layout Component Patterns in Next.js
Remi W.
Remi W.

Posted on • Originally published at creativedesignsguru.com

Currying Layout Component Patterns in Next.js

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. But, the first time I saw this pattern was from Adam Wathan's blog post about Persistent Layout Patterns in Next.js. 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:

const getFooter = (page: ReactElement) => (
  <Footer>{page}</Footer>
);
Enter fullscreen mode Exit fullscreen mode

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

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

Page.getLayout = getFooter;
Enter fullscreen mode Exit fullscreen mode

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

Next.js layout component

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:

const getDashboardChangeEmail = (page: ReactElement) => (
  <AuthProvider>
    <StateProvider>
      <Dashboard title="Change Email">{page}</Dashboard>
    </StateProvider>
  </AuthProvider>
);
Enter fullscreen mode Exit fullscreen mode
const getDashboardChangePassword = (page: ReactElement) => (
  <AuthProvider>
    <StateProvider>
      <Dashboard title="Change password">{page}</Dashboard>
    </StateProvider>
  </AuthProvider>
);
Enter fullscreen mode Exit fullscreen mode

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:

const getDashboard = (title: title) => (page: ReactElement) => (
  <AuthProvider>
    <StateProvider>
      <Dashboard title={title}>{page}</Dashboard>
    </StateProvider>
  </AuthProvider>
);
Enter fullscreen mode Exit fullscreen mode

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

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

Page.getLayout = getDashboard('Change Email');
Enter fullscreen mode Exit fullscreen mode
// pages/change-password.js
export default function Page() {
  return {
    /** Your content */
  }
}

Page.getLayout = getDashboard('Change Password');
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)