Урок 03

Layouts и вложенность

Layout — общий каркас для группы страниц. Сохраняется между переходами, не перерисовывается.

Root layout

Обязателен. Оборачивает всё приложение.

app/layout.tsx:

export default function RootLayout({ children }: { children: React.ReactNode }) {
    return (
        <html lang="ru">
            <body>
                <header>Шапка</header>
                {children}
                <footer>Подвал</footer>
            </body>
        </html>
    );
}

Должен содержать <html> и <body>.

Вложенный layout

app/blog/layout.tsx:

export default function BlogLayout({ children }: { children: React.ReactNode }) {
    return (
        <div className="blog">
            <aside>Сайдбар блога</aside>
            <main>{children}</main>
        </div>
    );
}

Применяется ко всем страницам в app/blog/.

Вложенность

app/
├── layout.tsx               ← root
├── page.tsx
└── blog/
    ├── layout.tsx           ← blog
    ├── page.tsx
    └── [id]/
        └── page.tsx

Страница /blog/1 получит:

root layout
└── blog layout
    └── page

Не перерисовывается

Layout сохраняется при переходах между страницами. Меняется только children. Это быстрее и сохраняет состояние.

template.tsx

Как layout, но пересоздаётся на каждом переходе:

// app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
    return <div>{children}</div>;
}

Используется для анимаций входа, useEffect при смене страницы.

loading.tsx

Показывается во время загрузки страницы:

// app/blog/loading.tsx
export default function Loading() {
    return <p>Загрузка...</p>;
}

Автоматически оборачивает страницу в <Suspense>.

error.tsx

Ловит ошибки:

'use client';

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
    return (
        <div>
            <h2>Что-то пошло не так</h2>
            <p>{error.message}</p>
            <button onClick={reset}>Попробовать снова</button>
        </div>
    );
}

Должен быть клиентским компонентом.

not-found.tsx

// app/not-found.tsx
export default function NotFound() {
    return <h1>404 — не найдено</h1>;
}

Вызывается через notFound():

import { notFound } from 'next/navigation';

const post = await getPost(id);
if (!post) notFound();

Комбинирование

app/
├── layout.tsx
├── loading.tsx
├── error.tsx
├── not-found.tsx
└── blog/
    ├── layout.tsx
    ├── loading.tsx
    └── page.tsx

Каждый уровень может иметь свои.

Итоги

  • layout.tsx — общий каркас
  • Root layout обязателен
  • Вложенные layouts применяются по цепочке
  • Layout не перерисовывается
  • template.tsx пересоздаётся
  • loading, error, not-found — специальные файлы