JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Layout-система в Next.js 15: layout.tsx, template.tsx и и...

Layout-система в Next.js 15: layout.tsx, template.tsx и их отличие

Модуль 4: Node.js, Next.js и Angular
8 уровень , 8 лекция
Открыта

1. Углубляемся в layout.tsx

Если вы когда-нибудь пытались реализовать “шапку” и “подвал” (header и footer), которые видны на всех страницах сайта, вы наверняка сталкивались с проблемой: как не копировать один и тот же код в каждую страницу? В классическом React (или в Page Router Next.js) это обычно делается через компонент-обёртку, но в App Router появился свой, более мощный и удобный способ — layout-система.

Layout — это компонент, который определяет “скелет” для группы страниц. Он автоматически применяется ко всем страницам и дочерним layouts, находящимся в той же папке или ниже по структуре.

template — это специальный компонент, похожий на layout, но с другим поведением: он создаёт новый экземпляр при каждой навигации, а layout — сохраняет один и тот же.

Давайте разберёмся подробнее!

Где и как создаётся layout.tsx?

В любой папке внутри app/ вы можете (и должны!) создать файл layout.tsx. Это файл, который определяет, как будут выглядеть все вложенные страницы и layouts внутри этой папки.


app/
  layout.tsx       // главный layout для всего приложения
  page.tsx         // главная страница ("/")
  about/
    layout.tsx     // layout только для /about и его детей
    page.tsx       // страница "/about"
    team/
      page.tsx     // страница "/about/team"

Пояснение:
app/layout.tsx — применяется ко всему приложению.
app/about/layout.tsx — применяется только к /about и всем его потомкам (например, /about/team).

Пример простого layout.tsx


// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html lang="ru">
      <body>
        <header>Мой сайт</header>
        <main>{children}</main>
        <footer>© 2024</footer>
      </body>
    </html>
  );
}

Пояснение:
children — это всё содержимое, которое вложено в этот layout (страницы, другие layouts, templates).
Layout должен возвращать валидную структуру HTML. Обычно это <html>, <body>, header/footer и место для children.

Особенности layout.tsx

  • Layout автоматически применяется ко всем вложенным страницам.
  • Layout кэшируется: при переходе между страницами, которые используют один и тот же layout, его компонент не пересоздаётся (не сбрасывается state, не пересоздаются эффекты).
  • Можно создавать layout на любом уровне вложенности: для всего приложения, для секций, для отдельных групп маршрутов.

Как работает вложенность layout'ов?

Layout'ы можно вкладывать друг в друга. Например, если у вас есть layout для всего сайта и отдельный layout для секции “about”, то структура рендеринга будет примерно такой:


RootLayout
  └── AboutLayout
        └── AboutPage (или TeamPage и т.д.)
Вложенность layout'ов

Каждый layout получает в props свой children — это всё, что ниже по иерархии.

2. template.tsx: “Свежий” шаблон на каждый переход

Что такое template.tsx?

template.tsx — это ещё один специальный файл App Router, который похож на layout, но с важным отличием: template пересоздаётся при каждом переходе на новую страницу внутри своей области.

Зачем это нужно? Иногда вы хотите, чтобы при переходе между страницами сбрасывался state, перезапускались эффекты, заново запускались анимации и т.д. Layout для этого не подходит, потому что он сохраняет своё состояние между переходами.

Пример использования template.tsx


// app/about/template.tsx
export default function AboutTemplate({ children }) {
  return (
    <section style={{ border: "2px solid blue", padding: "1rem" }}>
      <h2>Секция "О нас"</h2>
      {children}
    </section>
  );
}

Пояснение:
Все страницы и компоненты внутри /about будут обёрнуты в этот шаблон.
Каждый раз, когда вы переходите на новую страницу внутри /about, экземпляр AboutTemplate будет создаваться заново.

Когда нужен template.tsx, а не layout.tsx?

  • Если внутри обёртки есть локальный state, который должен сбрасываться при каждом переходе (например, модальные окна, формы, временные данные).
  • Если нужны анимации “появления” страницы.
  • Если нужно, чтобы компонент пересоздавался при навигации, а не сохранял старое состояние.

3. Сравнение layout.tsx и template.tsx

Характеристика layout.tsx template.tsx
Жизненный цикл Один раз при монтировании, сохраняется между переходами Пересоздаётся при каждом переходе
Сброс состояния State сохраняется State сбрасывается
Использование Для общего “скелета” (header, sidebar, footer) Для анимаций, сброса состояния, “шаблонов” страниц
Вложенность Можно вкладывать друг в друга Можно вкладывать, но обычно используется локально
Пример
<body><Header />{children}<Footer /></body>
<section>{children}</section>

Аналогия:
Layout — это как стены и крыша дома: они всегда одни и те же, даже если вы переставляете мебель.
Template — это как декорации на вечеринке: каждый раз, когда приходит новая компания гостей, вы всё снимаете и заново украшаете комнату.

4. Как layout и template работают вместе: схема рендеринга

Представим структуру:


app/
  layout.tsx
  about/
    layout.tsx
    template.tsx
    page.tsx
    team/
      page.tsx
Пример вложенности layout'ов и template

При переходе с /about на /about/team:

  • Root layout (app/layout.tsx) — не пересоздаётся.
  • About layout (about/layout.tsx) — не пересоздаётся.
  • About template (about/template.tsx) — пересоздаётся!
  • Страница — пересоздаётся, естественно.

app/layout.tsx
  └─ about/layout.tsx
      └─ about/template.tsx (пересоздаётся при каждом переходе)
          └─ page.tsx или team/page.tsx

5. Пример: layout.tsx и template.tsx в действии

layout.tsx с навигацией и футером


// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html lang="ru">
      <body>
        <nav>
          <a href="/">Главная</a> | <a href="/about">О нас</a>
        </nav>
        <hr />
        {children}
        <footer>© 2024 Все права защищены</footer>
      </body>
    </html>
  );
}

about/layout.tsx: layout для секции


// app/about/layout.tsx
export default function AboutLayout({ children }) {
  return (
    <div style={{ background: "#e0f7fa", padding: "1rem" }}>
      <h1>О нас</h1>
      {children}
    </div>
  );
}

about/template.tsx: сброс состояния


// app/about/template.tsx
import { useState } from "react";

export default function AboutTemplate({ children }) {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Локальный счетчик: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <div>{children}</div>
    </div>
  );
}

Пояснение:
При переходе между /about и /about/team счётчик будет сбрасываться на 0, потому что template пересоздаётся.
Layout (и root layout, и about layout) — не сбрасывают state, даже если он там есть.

6. Типичные ошибки при работе с layout.tsx и template.tsx

Ошибка №1: Ожидание сброса состояния в layout, а не в template.
Многие новички думают, что если поместить useState в layout, то он будет сбрасываться при переходах между страницами. На самом деле, layout сохраняет своё состояние, и сброса не произойдёт. Если вам нужно сбрасывать state — используйте template.tsx.

Ошибка №2: Отсутствие обязательной структуры в layout.tsx.
Главный layout (app/layout.tsx) должен возвращать <html> и <body>. Если этого не сделать, приложение может вести себя странно (например, некорректно работать с метаданными, стилями и т.д.).

Ошибка №3: Дублирование кода между layout и template.
Иногда разработчики копируют один и тот же код и в layout, и в template. Лучше разделять: layout — для “скелета”, template — для сбрасываемых обёрток.

Ошибка №4: Хранение глобального состояния в template.
Если вы поместите, например, Redux Provider или Context Provider в template, состояние будет сбрасываться при каждом переходе между страницами. Обычно глобальные провайдеры должны быть в layout.

Ошибка №5: Неочевидная вложенность layouts и templates.
Иногда сложно понять, какой layout или template применяется к какой странице. Лайфхак: смотрите на структуру папок — layout и template применяются ко всем файлам внутри своей папки и вложенных папок, пока не встретится новый layout/template.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ