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

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

Специальные файлы layout.js, page.js и template.js позволяют создавать пользовательский интерфейс для маршрута. Это руководство объяснит, как и когда использовать эти специальные файлы.

Страницы

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

Например, чтобы создать index страницу, добавьте файл 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>
}

Чтобы создать дополнительные страницы, создайте новую папку и добавьте в неё файл page.js. Например, для маршрута /dashboard создайте папку dashboard и добавьте в неё файл page.js:

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

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

Макеты

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

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

Например, макет будет общим для страниц /dashboard и /dashboard/settings:

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>
  )
}

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

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

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* UI макета */}
        <main>{children}</main>
      </body>
    </html>
  )
}
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {/* UI макета */}
        <main>{children}</main>
      </body>
    </html>
  )
}

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

По умолчанию макеты в иерархии папок вложены, то есть они оборачивают дочерние макеты через проп children. Вы можете вкладывать макеты, добавляя layout.js в определённые сегменты маршрутов (папки).

Например, чтобы создать макет для маршрута /dashboard, добавьте новый файл layout.js в папку dashboard:

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

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

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

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

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

Шаблоны

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

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

  • Функции, зависящие от useEffect (например, логирование просмотров страниц) и useState (например, форма обратной связи для каждой страницы).
  • Для изменения поведения фреймворка по умолчанию. Например, границы Suspense внутри макетов показывают 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 рендерится между макетом и его дочерними элементами. Вот упрощённый вывод:

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

Метаданные

В директории app вы можете изменять HTML-элементы <head>, такие как title и meta, используя API Метаданных.

Метаданные можно определить, экспортировав объект 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>, в корневые макеты. Вместо этого используйте API Метаданных, который автоматически обрабатывает сложные требования, такие как потоковая передача и дедупликация элементов <head>.

Подробнее о доступных опциях метаданных см. в справочнике API