Key Takeaways
- Shared Next.js layouts keep navigation and page structure consistent without duplicating markup.
- Use root and nested
layout.tsxfiles in the App Router; usepages/_app.jsorgetLayoutfor persistent Pages Router layouts. childrenrepresents the content nested inside a component’s opening and closing tags.- Sharing layout code doesn’t automatically preserve layout state. Where the layout sits in the component tree matters.
- Use
next/headin the Pages Router and the Metadata API in the App Router.
Next.js layouts share common UI—navigation, sidebars, and page structure—across routes. In the App Router, define that UI in layout.tsx files; in the Pages Router, compose ordinary React components and lift persistent layouts into _app.js. The original page-wrapper approach remains useful, but it isn’t the App Router’s layout convention.
We’ll start with the simple Nav-plus-content example, then make it persistent and show the App Router equivalent. These examples assume an existing Next.js project; see the Next.js starter tutorial if you need the surrounding setup.
Choose a Layout Pattern for Your Router
Choose by router first, then by whether shared UI needs to retain state between pages.
| Situation | Pattern | Metadata |
|---|---|---|
| App Router application | Root and nested layout.tsx files | metadata or generateMetadata |
| Pages Router with a persistent shell | pages/_app.js, optionally with getLayout | next/head |
| Pages Router with page-local composition | Wrap page content in <MyLayout> | next/head |
The shared UI lives at different levels:
Pages Router App Router
pages/_app.js app/layout.tsx
+-- MyLayout +-- app/dashboard/layout.tsx
+-- active page +-- active dashboard pagePages Router: Share Navigation With children
This section uses pages/, not app/. Create the following files in a Pages Router project.
Create Nav and MyLayout
The draft input makes state preservation easy to test later. Next.js Link provides client-side navigation between routes.
// components/Nav.jsx
import Link from 'next/link';
import { useState } from 'react';
export default function Nav() {
const [draft, setDraft] = useState('');
return (
<nav aria-label="Primary">
<Link href="/">Home</Link>{' | '}
<Link href="/dashboard">Dashboard</Link>
<label>
Draft
<input
value={draft}
onChange={(event) => setDraft(event.target.value)}
/>
</label>
</nav>
);
}// components/MyLayout.jsx
import Nav from './Nav';
export default function MyLayout({ children }) {
return (
<div>
<Nav />
<main>{children}</main>
</div>
);
}children represents everything nested inside <MyLayout>...</MyLayout>. It’s a normal prop supplied through JSX nesting, not a Next.js-specific feature. React’s guide to passing props explains this composition model; our React components tutorial covers the foundations.
Wrap Individual Pages
// pages/index.jsx
import Head from 'next/head';
import MyLayout from '../components/MyLayout';
export default function Home() {
return (
<MyLayout>
<Head>
<title>Home</title>
</Head>
<p>Hello</p>
</MyLayout>
);
}// pages/dashboard.jsx
import MyLayout from '../components/MyLayout';
export default function Dashboard() {
return <MyLayout><p>Dashboard</p></MyLayout>;
}Nav now has one implementation. Changing its links updates both pages: that’s DRY—“Don’t Repeat Yourself”—applied to page structure. next/head remains the Pages Router API for the title shown above.
However, in this example, switching between the two page components remounts the layout beneath them and resets the draft. Reusing component code is different from keeping a component mounted. React preserves state according to a component’s identity and position in the rendered tree.
Pages Router: Preserve Layout State With _app.js
For one shared application shell, move MyLayout above the active page in a custom App component:
// pages/_app.js
import MyLayout from '../components/MyLayout';
export default function App({ Component, pageProps }) {
return (
<MyLayout>
<Component {...pageProps} />
</MyLayout>
);
}Remove the MyLayout imports and wrappers from both page files. Each page should return only its own content; the home page can keep its Head component alongside its paragraph in a fragment.
Component is the active page, and pageProps contains its initial props. Forwarding pageProps ensures the page receives data supplied by Next.js. The outer MyLayout can remain mounted while Component changes.
Type into the draft input, then follow the navigation links. The draft should remain during these client-side transitions. If you’ve just added _app.js to a running project, restart the development server.
Use getLayout for Different or Nested Layouts
When some pages need additional structure, use the documented getLayout pattern. Replace the previous _app.js with:
// pages/_app.js
import MyLayout from '../components/MyLayout';
export default function App({ Component, pageProps }) {
const getLayout =
Component.getLayout ?? ((page) => <MyLayout>{page}</MyLayout>);
return getLayout(<Component {...pageProps} />);
}Then replace the dashboard page with a page that supplies its own nested structure:
// pages/dashboard.jsx
import MyLayout from '../components/MyLayout';
export default function Dashboard({ message }) {
return <p>{message}</p>;
}
Dashboard.getLayout = function getLayout(page) {
return (
<MyLayout>
<section aria-label="Dashboard">
<h2>Workspace</h2>
{page}
</section>
</MyLayout>
);
};
export async function getStaticProps() {
return { props: { message: 'Content for my layout!' } };
}getLayout is a property your custom App reads, not an automatically executed page export. The fallback and dashboard both return MyLayout at the same outer position, allowing its state to survive navigation between them. Changing that outer component type or its key can reset its state.
App Router: Root and Nested Next.js Layouts
In the App Router, layout files provide the nesting automatically. Use the following as a separate App Router example, rather than adding it alongside the Pages Router sample.
Create the Root Layout
The root layout must include <html> and <body>. Define shared navigation there, and render the active page or nested layout through children.
// app/layout.tsx
import type { Metadata } from 'next';
import type { ReactNode } from 'react';
import Link from 'next/link';
export const metadata: Metadata = {
title: 'Layout demo',
description: 'Shared navigation and nested layouts.',
};
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<nav aria-label="Primary">
<Link href="/">Home</Link>{' | '}
<Link href="/dashboard">Dashboard</Link>
</nav>
<main>{children}</main>
</body>
</html>
);
}// app/page.tsx
export default function Home() {
return <p>Hello</p>;
}Add a Dashboard Layout and Child Pages
// app/dashboard/layout.tsx
import type { ReactNode } from 'react';
import Link from 'next/link';
export default function DashboardLayout({ children }: { children: ReactNode }) {
return (
<section aria-label="Dashboard">
<aside>
<h2>Workspace</h2>
<label>Dashboard draft <input /></label>
<nav aria-label="Dashboard">
<Link href="/dashboard">Overview</Link>{' | '}
<Link href="/dashboard/settings">Settings</Link>
</nav>
</aside>
<div>{children}</div>
</section>
);
}// app/dashboard/page.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = { title: 'Dashboard' };
export default function Dashboard() {
return <p>Dashboard overview</p>;
}// app/dashboard/settings/page.tsx
export default function Settings() {
return <p>Dashboard settings</p>;
}The root layout wraps the dashboard layout, which wraps either dashboard page. Type into the dashboard draft and switch between Overview and Settings: the shared dashboard layout persists. No page-level <DashboardLayout> wrapper or getLayout assignment is needed.
Use Metadata Instead of next/head
The metadata exports above define static metadata. For metadata derived from route parameters or fetched data, export generateMetadata instead. Both APIs are supported in Server Components, and you can’t export both from the same route segment. See the Metadata API reference.
Alternative Layout Techniques
A Higher-Order Component That Forwards Props
A higher-order component, or HOC, takes a component and returns another component. You may encounter this pattern in existing applications; for layout markup alone, I’d choose children or the router’s persistent-layout mechanism.
This corrected version uses a distinct module and forwards the incoming page props:
// components/withLayout.jsx
import MyLayout from './MyLayout';
export default function withLayout(Page) {
return function WithLayout(pageProps) {
return (
<MyLayout>
<Page {...pageProps} />
</MyLayout>
);
};
}// pages/index.jsx
// Alternative to the earlier page; don't also wrap it in _app.js.
import withLayout from '../components/withLayout';
function PageContent({ message = 'Content for my layout!' }) {
return <p>{message}</p>;
}
export default withLayout(PageContent);Apply the HOC at module scope, not inside a render function. It doesn’t make the layout persistent across different page components or automatically copy properties such as getLayout. React’s legacy HOC documentation explains these identity and property-copying caveats.
Keep getStaticProps and getServerSideProps as named exports from the page module. Forwarding props doesn’t replace those exports.
Pass Content Through Named Props
You can also pass page content explicitly. Named props earn their place when a component has multiple content regions, such as a sidebar and the main content:
// components/ContentLayout.jsx
import MyLayout from './MyLayout';
export default function ContentLayout({ content, sidebar }) {
return (
<MyLayout>
<aside>{sidebar}</aside>
<div>{content}</div>
</MyLayout>
);
}// pages/index.jsx
// Another alternative to manual children-based page wrapping.
import ContentLayout from '../components/ContentLayout';
const pageContent = <p>Hello Next.js</p>;
export default function Home() {
return (
<ContentLayout
content={pageContent}
sidebar={<p>Related documentation</p>}
/>
);
}For one content region, prefer children: it communicates ordinary nesting without inventing another prop name. You can combine named slots with children; neither convention determines whether the component persists.
Context Providers and Common Mistakes
React Context shares a value with descendant consumers. Place its provider above every component that needs the value: wrapping only page content won’t make that context available to navigation outside the provider.
- Pages Router: place an application-wide provider in
_app.js, outside the layout if navigation also consumes it. A provider inside a page can reset when that page remounts. - App Router: implement the provider in a separate Client Component with
'use client', then render it from the appropriate layout. Keep it as deep as its consumers allow.
App Router layouts and pages are Server Components by default. A server layout can render a client provider without becoming a Client Component itself, but Server Components can’t consume that client context. Keep stateful controls in small Client Components instead of marking the whole layout as client code.
For more on the underlying state model, see React props and state.
- Don’t confuse persistence with durable storage. A full reload resets in-memory React state.
- Don’t add changing keys to a persistent shell. A different key can intentionally reset its state.
- Don’t read changing URL state once in a shared server layout. For active navigation, use
usePathnamefromnext/navigationin a small Client Component; shared layouts don’t rerender on ordinary navigation. - Don’t use hidden navigation as authorization. Enforce access checks near protected data and in server-side mutation entry points, not only in a layout.
The layout reference covers URL-state limitations, while the authentication guide explains why layout-only access checks are insufficient.
FAQ
How do I create a shared layout in the Next.js App Router?
Default-export a component from app/layout.tsx, accept children, and include the required html and body elements. Add layout.tsx files inside route folders to share additional UI within those route segments.
What is the difference between app/layout.tsx and pages/_app.js?
app/layout.tsx participates in the App Router’s built-in layout hierarchy and defines the root document structure. pages/_app.js wraps active Pages Router pages and can implement a shared shell or dispatch to each page’s getLayout function.
How can I use different layouts for different pages in Next.js?
In the App Router, add nested layout files around the route segments that share UI. In the Pages Router, have your custom App call a page’s getLayout function, with a fallback layout for pages that don’t define one.
Do Next.js layouts persist between page navigations?
Shared App Router layouts persist during client-side navigation within their scope. Pages Router layouts can preserve state when kept above the changing page through _app.js or a consistent getLayout tree; wrapping each page independently doesn’t provide that persistence.
What if my layout needs React Context?
Place the provider above all consumers and at a persistent level if its state should survive navigation. In the App Router, define the provider in a Client Component and render it from the layout; Server Components can’t consume that client context.
Further Reading
- App Router layouts and pages — root layouts, route segments, and automatic nesting.
- Pages Router layout patterns — shared shells and per-page getLayout examples.
- Layout file reference — root requirements and navigation caveats.
- Metadata API — static metadata and generated page metadata.
- Server and Client Components — interactive boundaries and provider placement.
- React state preservation — how component identity, tree position, and keys affect state.
- React props and children — the composition model behind reusable layouts.
- Next.js authentication guidance — authorization beyond shared layout UI.
