Layouts: The App Router Superpower
Layouts wrap pages in persistent UI that does not re-render on navigation. This makes it trivial to have a shell that stays mounted while the content changes — a pattern that previously required complex setup in single-page applications.
Root Layout
Every App Router project requires a root layout:
// app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: {
template: '%s | My App',
default: 'My App',
},
description: 'Built with Next.js 15',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<header>
<nav>/* Global navigation */</nav>
</header>
<main>{children}</main>
<footer>/* Global footer */</footer>
</body>
</html>
);
}
Nested Layouts
Layouts compose. A dashboard layout adds a sidebar without affecting the root layout:
// app/(dashboard)/layout.tsx
import { Sidebar } from '@/components/sidebar';
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex h-screen">
<Sidebar className="w-64 shrink-0" />
<div className="flex-1 overflow-y-auto p-8">
{children}
</div>
</div>
);
}
Metadata Per Layout
Each layout and page can export metadata that gets merged:
// app/(dashboard)/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | Dashboard',
default: 'Dashboard',
},
};
// app/(dashboard)/settings/page.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Settings', // Renders as "Settings | Dashboard | My App"
};
Template vs Layout
Use template.tsx instead of layout.tsx when you want the wrapper to re-mount on each navigation (useful for page transition animations):
// app/blog/template.tsx — re-mounts on every blog navigation
export default function BlogTemplate({ children }: { children: React.ReactNode }) {
return (
<div className="animate-fade-in">
{children}
</div>
);
}
Layouts are one of the most powerful features of the App Router — use them to eliminate repetitive wrapper components across your pages.