Creating Different Layouts for Different NextJS Pages

Creating Different Layouts for Different NextJS Pages

September 18, 2023

3 min read

Creating Different Layouts for Different NextJS Pages
Watch on YouTube

In this post, we will learn how to create different layouts for different NextJS pages. We'll examine how this is done at increaser.org, which incorporates four layout variations. Although Increaser's repository is private, you can find all the code in the RadzionKit repository as it also uses different layouts for the landing page and the remaining pages.

Why not use layout components directly in the page component?

We could use different layout components directly in the page component. However, the issue arises when we navigate between pages with the same layout. The layout ends up being re-rendered. This becomes problematic when you have a sidebar layout with a scroll, and, as you scroll down and move to another page, the scroll resets. Thus, we need a method that allows different layouts for different pages, while also maintaining the layout when we navigate between pages with the same layout for improved user experience.

Sidebar
Sidebar

How to create different layouts for different pages?

At Increaser, we implement four different layouts:

  • Authorization pages that have the same top bar as the landing page.
  • App pages with the sidebar.
  • Project pages that also contain additional navigation between projects.
  • A full-screen focus page that lacks any navigation.

To accomplish this, we use three helper functions: makeAppPage, makeAuthPage, and makeProjectsPage.

import { Page, GetLayout } from "layout/Page"
import { AppPageLayout } from "focus/components/AppPageLayout"

const getAppPageLayout: GetLayout = (page) => (
  <AppPageLayout>{page}</AppPageLayout>
)

export const makeAppPage = (page: Page) => {
  page.getLayout = getAppPageLayout

  return page
}

All of these functions look alike. Each is a function that takes a component and adds a getLayout function to it, which wraps the page within a specific layout.

import { NextPage } from "next"
import { ReactNode } from "react"

export type GetLayout = (page: ReactNode) => ReactNode

export type Page<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: GetLayout
}

The Page type is simply a NextJS page with an extra function. The GetLayout type describes a function that takes one React node and returns another React node.

How to use page layout helpers?

Here is how we use our helpers: we wrap the page component with the helper function, as exemplified in this sign-up page.

import { SignUpContent } from "auth/components/SignUpContent"
import { makeAuthPage } from "layout/makeAuthPage"

export default makeAuthPage(SignUpContent)

Without the helper, we would have more code to write:

import { SignUpContent } from "auth/components/SignUpContent"
import { AppPageLayout } from "focus/components/AppPageLayout"
import { Page } from "layout/Page"

const SignUpPage: Page = SignUpContent

export default SignUpPage

SignUpPage.getLayout = (page) => <AppPageLayout>{page}</AppPageLayout>

_app.tsx and getLayout

In which context do we call the getLayout function?

function MyApp({ Component, pageProps }: MyAppProps) {
  const getLayout = Component.getLayout || ((page: ReactNode) => page)
  const component = getLayout(<Component {...pageProps} />)

  return (
    <ThemeProvider>
      <GlobalStyle fontFamily={inter.style.fontFamily} />
      {component}
    </ThemeProvider>
  )
}

export default MyApp

It will be the _app.tsx file that calls the getLayout function. If the page lacks a getLayout function like the focus page at Increaser, it will simply return the page component.