Skip to content
C.W.K.
Stream
Lesson 03 of 10 · published

layout.tsx — Shared UI

~20 min · layout, persistence, root layout

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Layouts wrap and persist

A layout.tsx wraps every route at and below its segment. The crucial property: layouts persist across navigation. When a user clicks between sibling pages, the layout component does not unmount. Sidebars, navigation, and any state in the layout stay alive.

The required root layout

Every Next.js app needs a root layout at app/layout.tsx. It must render the <html> and <body> tags — nowhere else does. This is where global font, language attribute, and the body class live.

Layout props

Layouts take children (the nested page or layout) and, in v15+, params as a Promise when the layout is inside a dynamic segment.

What layouts can't do

Layouts don't receive searchParams. If a layout depends on the query string, restructure: pass the data through the URL into a Server Component inside the page, not the layout.

Place something in a layout only when every descendant needs it and it should survive navigation between those descendants. Global navigation and a dashboard sidebar qualify; a page-specific filter usually does not. This lifetime test is more reliable than deciding from visual similarity. A shared-looking component is not automatically root-layout material. Providers and data placed too high become a cost paid by unrelated routes, while state that should reset can linger. Use the nearest common layout that owns the responsibility, not the widest ancestor that happens to work. Give the layout and page separate pieces of local state, move between sibling and unrelated routes, and record what mounts and resets. Change only the query string as well. The observed lifetime should match the product requirement before you add more state management.

Code

Required root layout·tsx
// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className="min-h-screen bg-white text-gray-900 antialiased">
        <SiteNav />
        <main className="mx-auto max-w-5xl p-6">{children}</main>
        <SiteFooter />
      </body>
    </html>
  );
}
Nested layout with awaited params·tsx
// app/team/[teamId]/layout.tsx
export default async function TeamLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: Promise<{ teamId: string }>;
}) {
  const { teamId } = await params;
  return (
    <div className="grid grid-cols-[200px_1fr] gap-6">
      <TeamSidebar teamId={teamId} />
      {children}
    </div>
  );
}

External links

Exercise

Add a layout for your /dashboard/… routes that includes a sidebar with nav links. Click between two child pages and confirm the sidebar's local state (e.g., a collapse toggle) survives navigation.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.