Страницы и макеты

Рекомендуем прочитать разделы Основы маршрутизации и Определение маршрутов перед продолжением.

App Router в Next.js 13 представил новые соглашения по файлам для удобного создания страниц, общих макетов и шаблонов. Это руководство покажет, как использовать эти специальные файлы в вашем приложении Next.js.

Страницы

Страница — это пользовательский интерфейс, уникальный для маршрута. Вы можете определить страницы, экспортируя компонент из файла page.js. Используйте вложенные папки для определения маршрута и файл page.js, чтобы сделать маршрут общедоступным.

Создайте свою первую страницу, добавив файл page.js в директорию app:

Специальный файл page.js
// `app/page.tsx` — это UI для URL `/`
export default function Page() {
  return <h1>Привет, домашняя страница!</h1>
}
// `app/page.js` — это UI для URL `/`
export default function Page() {
  return <h1>Привет, домашняя страница!</h1>
}
// `app/dashboard/page.tsx` — это UI для URL `/dashboard`
export default function Page() {
  return <h1>Привет, страница Dashboard!</h1>
}
// `app/dashboard/page.js` — это UI для URL `/dashboard`
export default function Page() {
  return <h1>Привет, страница Dashboard!</h1>
}

Полезно знать:

Макеты

Макет — это пользовательский интерфейс, общий для нескольких страниц. При навигации макеты сохраняют состояние, остаются интерактивными и не перерендериваются. Макеты также могут быть вложенными.

Вы можете определить макет, экспортируя React-компонент по умолчанию из файла layout.js. Компонент должен принимать проп children, который будет заполнен дочерним макетом (если он существует) или страницей во время рендеринга.

Специальный файл layout.js
export default function DashboardLayout({
  children, // будет страницей или вложенным макетом
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* Разместите общий UI здесь, например, шапку или боковую панель */}
      <nav></nav>

      {children}
    </section>
  )
}
export default function DashboardLayout({
  children, // будет страницей или вложенным макетом
}) {
  return (
    <section>
      {/* Разместите общий UI здесь, например, шапку или боковую панель */}
      <nav></nav>

      {children}
    </section>
  )
}

Полезно знать:

  • Самый верхний макет называется Корневым макетом (Root Layout). Этот обязательный макет используется всеми страницами приложения. Корневой макет должен содержать теги html и body.
  • Любой сегмент маршрута может опционально определить свой собственный макет. Эти макеты будут общими для всех страниц в этом сегменте.
  • Макеты в маршруте по умолчанию вложены. Каждый родительский макет оборачивает дочерние макеты с помощью пропа children в React.
  • Вы можете использовать Группы маршрутов (Route Groups) для включения или исключения определенных сегментов маршрута из общих макетов.
  • Макеты по умолчанию являются серверными компонентами (Server Components), но могут быть настроены как клиентские компоненты (Client Components).
  • Макеты могут получать данные. Подробнее см. в разделе Получение данных.
  • Передача данных между родительским макетом и его дочерними элементами невозможна. Однако вы можете получать одни и те же данные в маршруте несколько раз, и React автоматически устранит дублирование запросов без ущерба для производительности.
  • Макеты не имеют доступа к сегментам маршрута ниже себя. Для доступа ко всем сегментам маршрута можно использовать useSelectedLayoutSegment или useSelectedLayoutSegments в клиентском компоненте.
  • Для макетов можно использовать расширения файлов .js, .jsx или .tsx.
  • В одной папке могут быть определены файлы layout.js и page.js. Макет будет оборачивать страницу.

Корневой макет (обязательный)

Корневой макет определяется на верхнем уровне директории app и применяется ко всем маршрутам. Этот макет позволяет изменять исходный HTML, возвращаемый сервером.

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

Полезно знать:

Миграция из директории pages: Корневой макет заменяет файлы _app.js и _document.js. См. руководство по миграции.

Вложенные макеты

Макеты, определенные внутри папки (например, app/dashboard/layout.js), применяются к определенным сегментам маршрута (например, acme.com/dashboard) и рендерятся, когда эти сегменты активны. По умолчанию макеты в иерархии файлов вложены, то есть они оборачивают дочерние макеты через проп children.

Вложенный макет
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
  return <section>{children}</section>
}

Полезно знать:

  • Только корневой макет может содержать теги <html> и <body>.

Если объединить два макета выше, корневой макет (app/layout.js) будет оборачивать макет дашборда (app/dashboard/layout.js), который, в свою очередь, будет оборачивать сегменты маршрута внутри app/dashboard/*.

Два макета будут вложены следующим образом:

Вложенные макеты

Вы можете использовать Группы маршрутов (Route Groups) для включения или исключения определенных сегментов маршрута из общих макетов.

Шаблоны

Шаблоны похожи на макеты тем, что они оборачивают каждый дочерний макет или страницу. В отличие от макетов, которые сохраняются между маршрутами и поддерживают состояние, шаблоны создают новый экземпляр для каждого из своих дочерних элементов при навигации. Это означает, что при переходе между маршрутами, использующими один шаблон, монтируется новый экземпляр компонента, пересоздаются DOM-элементы, состояние не сохраняется, и эффекты синхронизируются заново.

В некоторых случаях такое поведение может быть полезно, и шаблоны будут более подходящим выбором, чем макеты. Например:

  • Функции, зависящие от useEffect (например, логирование просмотров страниц) и useState (например, форма обратной связи для каждой страницы).
  • Для изменения поведения фреймворка по умолчанию. Например, Suspense Boundaries внутри макетов показывают fallback только при первой загрузке макета, а не при переключении страниц. Для шаблонов fallback показывается при каждой навигации.

Шаблон можно определить, экспортируя React-компонент по умолчанию из файла template.js. Компонент должен принимать проп children.

Специальный файл template.js
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}
export default function Template({ children }) {
  return <div>{children}</div>
}

С точки зрения вложенности, template.js рендерится между макетом и его дочерними элементами. Вот упрощенный вывод:

Вывод
<Layout>
  {/* Обратите внимание, что шаблону присваивается уникальный ключ. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

Изменение <head>

В директории app вы можете изменять HTML-элементы <head>, такие как title и meta, с помощью встроенной поддержки SEO.

Метаданные можно определить, экспортируя объект metadata или функцию generateMetadata в файле layout.js или page.js.

import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}
export const metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}

Полезно знать: Не следует вручную добавлять теги <head>, такие как <title> и <meta>, в корневые макеты. Вместо этого используйте Metadata API, который автоматически обрабатывает сложные требования, такие как потоковая передача и устранение дублирования элементов <head>.

Узнайте больше о доступных опциях метаданных в справочнике API.