Adding a wrapper component to your Next.js app

By

Learn how to share a layout in Next.js 16 with app/layout.tsx, plus older HOC and props wrapper patterns for Pages Router apps.

~~~

To share a layout across pages in Next.js 16, use the App Router layout file. Put common chrome in app/layout.tsx (and nested layout.tsx files for sections). Each page fills in its own content through {children}.

Here’s the situation. All the pages on your site look more or less the same. There’s a chrome, a common base layer, and you just want to change what’s inside.

There’s a nav bar, a sidebar, and then the actual content. You don’t want to repeat the nav and sidebar in every page file.

App Router: app/layout.tsx

The default approach in Next.js 16 is a root layout:

// app/layout.js
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <div>
          <nav>
            <ul>....</ul>
          </nav>
          <main>{children}</main>
        </div>
      </body>
    </html>
  )
}

Every route under app/ gets this wrapper automatically. You don’t import the layout in each page. In a TypeScript project the file is app/layout.tsx and you type the prop as children: React.ReactNode, otherwise the compiler complains about an implicit any.

For a section that needs its own chrome (for example everything under /dashboard), add app/dashboard/layout.tsx. Nested layouts wrap the matching segment and still sit inside the root layout. See the Next.js overview for how the App Router file tree works.

You can still extract <Nav /> and <Sidebar /> into components and import them from the layout. CSS that belongs to the chrome goes next to those components or in the layout.

Optional: props wrapper (any router)

Sometimes you want an extra wrapper only on some pages, not the whole tree. A plain layout component that takes children (or a content prop) still works:

export default function Layout({ children }) {
  return (
    <div>
      <nav>
        <ul>....</ul>
      </nav>
      <main>{children}</main>
    </div>
  )
}
import Layout from '../components/Layout.js'

export default function Page() {
  return (
    <Layout>
      <p>Here's a page!</p>
    </Layout>
  )
}

Pages Router: HOC and getInitialProps gotchas

On older pages/ apps, people often used a Higher Order Component:

export default Page => {
  return () => (
    <div>
      <nav>
        <ul>....</ul>
      </nav>
      <main>
        <Page />
      </main>
    </div>
  )
}
import withLayout from '../components/Layout.js'

const Page = () => <p>Here's a page!</p>

export default withLayout(Page)

That breaks getInitialProps on the inner page. Next.js only calls getInitialProps on the default export. After withLayout(Page), the default export is the wrapper, so Page.getInitialProps never runs and your data props stay empty.

The props / children approach keeps the page as the default export, so getInitialProps still works:

import Layout from '../components/Layout.js'

const Page = () => (
  <Layout>
    <p>Here's a page!</p>
  </Layout>
)

Page.getInitialProps = ({ query }) => {
  //...
}

export default Page
Tagged: Next.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about next: