layout.tsx — Shared UI Across Segments

Wrap Pages With Shared Header, Sidebar, Providers

layout.tsx — Shared UI Across Segments

A layout wraps its segment and renders its children. Layouts persist across navigation, so state inside them stays alive.

4 min read Level 2/5 #nextjs#layout
What you'll learn
  • Create the root `app/layout.tsx` with html and body
  • Add nested layouts for inner segments
  • Understand why layouts persist across navigation

A layout is a component that wraps everything in its segment. Layouts compose, persist across navigation, and are where you put headers, sidebars, and providers.

The Root Layout

Every Next.js app needs a root layout. It is the only place to render <html> and <body>.

// app/layout.tsx
import './globals.css';

export const metadata = {
  title: 'My App',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <header>Nav</header>
        <main>{children}</main>
      </body>
    </html>
  );
}

The children prop is whatever route is being rendered.

Nested Layouts

Any segment can add its own layout.tsx. It wraps that segment’s children, in addition to the root layout.

// app/dashboard/layout.tsx — wraps everything under /dashboard
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="dashboard-shell">
      <nav>Dashboard Nav</nav>
      {children}
    </div>
  );
}

Now /dashboard/settings is wrapped by both the root layout and the dashboard layout.

Layouts Persist Across Navigation

When you navigate within a shared layout, that layout does not unmount. Any state inside it stays alive. This is the secret to fast, app-like transitions.

// app/dashboard/layout.tsx
'use client';
import { useState } from 'react';

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  // count survives navigation between /dashboard/* routes
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount((n) => n + 1)}>Clicked {count}</button>
      {children}
    </div>
  );
}

The next lesson covers how this all compares to the older Pages Router.

App Router vs Pages Router →