Вложенные layouts
Layouts создают иерархию обёрток. Применяются ко всем страницам внутри папки.
Иерархия
app/
├── layout.tsx ← root
├── page.tsx
├── dashboard/
│ ├── layout.tsx ← dashboard
│ ├── page.tsx
│ ├── settings/
│ │ └── page.tsx
│ └── profile/
│ └── page.tsx
└── blog/
├── layout.tsx ← blog
└── page.tsx
Страница /dashboard/settings получит:
root layout
└── dashboard layout
└── settings page
Root layout
Обязателен. Оборачивает всё.
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ru">
<body>{children}</body>
</html>
);
}
Должен содержать <html> и <body>.
Вложенный layout
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="dashboard">
<Sidebar />
<main>{children}</main>
</div>
);
}
Sidebar общий для всех страниц /dashboard/*.
Не перерисовывается
При переходе между /dashboard/settings и /dashboard/profile:
- Root layout сохраняется
- Dashboard layout сохраняется
- Меняется только page
Это быстрее и сохраняет состояние (скролл сайдбара, открытые меню).
Группы маршрутов
Разные layouts для разных секций:
app/
├── layout.tsx
├── (marketing)/
│ ├── layout.tsx ← маркетинговый layout
│ ├── about/page.tsx → /about
│ └── pricing/page.tsx → /pricing
└── (app)/
├── layout.tsx ← layout приложения
├── dashboard/page.tsx → /dashboard
└── settings/page.tsx → /settings
/about получит маркетинговый layout, /dashboard — layout приложения.
Получение params в layout
Layout не получает params дочерних страниц (в отличие от страниц). Но получает params своих динамических сегментов:
// app/blog/[category]/layout.tsx
export default async function Layout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ category: string }>;
}) {
const { category } = await params;
return (
<div>
<h2>Категория: {category}</h2>
{children}
</div>
);
}
Общая навигация
// app/dashboard/layout.tsx
import { NavLink } from '@/components/NavLink';
export default function DashboardLayout({ children }) {
return (
<>
<nav>
<NavLink href="/dashboard">Обзор</NavLink>
<NavLink href="/dashboard/settings">Настройки</NavLink>
<NavLink href="/dashboard/profile">Профиль</NavLink>
</nav>
<main>{children}</main>
</>
);
}
template.tsx
Как layout, но пересоздаётся на каждом переходе:
// app/dashboard/template.tsx
'use client';
export default function Template({ children }: { children: React.ReactNode }) {
return <div className="animate-in">{children}</div>;
}
Используется для анимаций, useEffect при смене страницы.
Modal через параллельные маршруты
Несколько layouts одновременно:
app/
├── layout.tsx
└── @modal/
└── default.tsx
└── posts/
└── [id]/
└── page.tsx
Сложная тема, но позволяет показывать модалку поверх страницы без потери URL.
Итоги
- Layouts вкладываются по цепочке
- Root layout обязателен
- Не перерисовываются при переходах
- Группы
(name)для разных layouts - Layout получает
paramsсвоих сегментов template.tsxпересоздаётся