Пошук уроків, статей та іншого контенту
Застосуєте template.tsx для створення повторно монтуємих шаблонів і керування поведінкою дочірніх маршрутів.
template.tsxtemplate.tsx — спеціальний файл App Router у Next.js, подібний до layout.tsx. Він також обгортає дочірні сторінки та маршрути, але має важливу відмінність:
layout.tsx зберігає стан під час навігації між дочірніми маршрутами;
template.tsx створюється заново під час навігації, тому його стан скидається;
ефекти в template.tsx запускаються повторно;
DOM дочірнього вмісту монтується заново.
Це корисно для інтерфейсів, які повинні починати новий життєвий цикл для кожного маршруту:
анімації переходів;
логування перегляду сторінки;
очищення локального стану форми;
повторне виконання ефектів;
тимчасові повідомлення або індикатори;
окремий стан для кожної сторінки в межах одного розділу.
Файл розміщується безпосередньо в директорії маршруту:
app/
├── layout.tsx
├── page.tsx
└── dashboard/
├── template.tsx
├── page.tsx
└── settings/
└── page.tsxapp/dashboard/template.tsx застосовується до сторінок усередині сегмента dashboard.
layout.tsx і template.tsxРозглянемо спрощену структуру:
app/
├── layout.tsx
└── dashboard/
├── layout.tsx
├── template.tsx
├── page.tsx
└── settings/
└── page.tsxВміст буде вкладено приблизно так:
app/layout.tsx
└── app/dashboard/layout.tsx
└── app/dashboard/template.tsx
└── app/dashboard/page.tsxПід час переходу між сторінками в межах dashboard:
app/layout.tsx зазвичай залишається змонтованим;
app/dashboard/layout.tsx зберігає свій стан;
app/dashboard/template.tsx монтується заново;
дочірня сторінка також монтується заново.
Тому template.tsx не слід використовувати для стану, який має зберігатися між дочірніми маршрутами. Для такого стану підходить layout.tsx.
Шаблон повинен експортувати React-компонент за замовчуванням і приймати children.
import type { ReactNode } from 'react';
type DashboardTemplateProps = {
children: ReactNode;
};
export default function DashboardTemplate({
children,
}: DashboardTemplateProps) {
return (
<section>
<header>Панель керування</header>
<main>{children}</main>
</section>
);
}За замовчуванням template.tsx є Server Component. Це означає, що в ньому не можна безпосередньо використовувати:
useState;
useEffect;
обробники подій;
браузерні API.
Якщо шаблону потрібна клієнтська поведінка, його можна позначити директивою 'use client'.
Створимо розділ dashboard із двома сторінками. Шаблон міститиме поле введення. При переході між сторінками значення поля буде очищено, оскільки template.tsx монтується заново.
app/layout.tsximport type { ReactNode } from 'react';
export default function RootLayout({
children,
}: {
children: ReactNode;
}) {
return (
<html lang="uk">
<body>{children}</body>
</html>
);
}app/page.tsximport Link from 'next/link';
export default function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<Link href="/dashboard">Відкрити панель керування</Link>
</main>
);
}app/dashboard/template.tsx'use client';
import { useEffect, useState, type ReactNode } from 'react';
import Link from 'next/link';
type DashboardTemplateProps = {
children: ReactNode;
};
export default function DashboardTemplate({
children,
}: DashboardTemplateProps) {
const [note, setNote] = useState('');
const [instanceId, setInstanceId] = useState('');
useEffect(() => {
const newInstanceId = crypto.randomUUID();
setInstanceId(newInstanceId);
console.log('Шаблон dashboard змонтовано:', newInstanceId);
return () => {
console.log('Шаблон dashboard демонтовано:', newInstanceId);
};
}, []);
return (
<section>
<header>
<h1>Панель керування</h1>
<nav aria-label="Навігація панелі">
<Link href="/dashboard">Огляд</Link>{' '}
<Link href="/dashboard/settings">Налаштування</Link>
</nav>
<label>
Тимчасова нотатка
<input
value={note}
onChange={(event) => setNote(event.target.value)}
placeholder="Введіть текст"
/>
</label>
<p>
Ідентифікатор екземпляра:{' '}
{instanceId || 'визначається після монтування'}
</p>
</header>
<main>{children}</main>
</section>
);
}app/dashboard/page.tsxexport default function DashboardPage() {
return (
<article>
<h2>Огляд</h2>
<p>Вміст головної сторінки панелі керування.</p>
</article>
);
}app/dashboard/settings/page.tsxexport default function SettingsPage() {
return (
<article>
<h2>Налаштування</h2>
<p>Вміст сторінки налаштувань.</p>
</article>
);
}Послідовність перевірки:
Відкрийте /dashboard.
Введіть текст у поле.
Перейдіть на /dashboard/settings.
Поверніться на /dashboard.
Після переходу значення note буде скинуто. Також у консолі браузера можна побачити новий ідентифікатор екземпляра та повторний запуск ефекту.
Основна властивість template.tsx — новий життєвий цикл компонента під час переходу.
Якщо шаблон є Client Component, його ефект:
useEffect(() => {
// Логіка після монтування
return () => {
// Очищення перед демонтуванням
};
}, []);буде виконуватися для кожного нового екземпляра шаблону.
Це можна використати для локальної ініціалізації:
'use client';
import { useEffect, type ReactNode } from 'react';
export default function Template({
children,
}: {
children: ReactNode;
}) {
useEffect(() => {
document.title = 'Новий розділ';
return () => {
document.title = 'Застосунок';
};
}, []);
return children;
}Однак ефект не повинен покладатися на те, що шаблон завжди буде єдиним екземпляром. Для важливих глобальних процесів потрібно окремо контролювати очищення ресурсів.
template.tsxШаблон діє в межах директорії, де він розташований.
app/
├── template.tsx
├── page.tsx
├── about/
│ └── page.tsx
└── dashboard/
└── page.tsxapp/template.tsx є спільним шаблоном для маршрутів застосунку.
app/
└── account/
├── template.tsx
├── page.tsx
└── security/
└── page.tsxapp/account/template.tsx застосовується до маршрутів у межах account.
Вкладені шаблони можуть формувати ланцюжок так само, як вкладені макети:
app/layout.tsx
└── app/account/layout.tsx
└── app/account/template.tsx
└── app/account/security/page.tsxtemplate.tsxВикористовуйте template.tsx, якщо дочірній маршрут повинен отримувати новий екземпляр обгортки.
Типові випадки:
форма має очищатися після переходу;
анімація повинна запускатися для кожної нової сторінки;
ефект повинен повторно виконуватися під час навігації;
потрібно створювати новий локальний контекст для кожного маршруту;
стан шаблону не має переходити на наступну сторінку.
Використовуйте layout.tsx, якщо потрібно:
зберігати стан бічної панелі;
не перемальовувати спільну навігацію;
зберігати відкриту вкладку;
підтримувати спільний стан між дочірніми сторінками;
уникати повторного монтування дорогих компонентів.
'use client';
import { useState, type ReactNode } from 'react';
export default function Template({
children,
}: {
children: ReactNode;
}) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<>
<button onClick={() => setIsMenuOpen((value) => !value)}>
Меню
</button>
{isMenuOpen && <aside>Меню відкрите</aside>}
{children}
</>
);
}Якщо меню повинно залишатися відкритим під час переходу між дочірніми сторінками, такий стан краще розмістити в layout.tsx, а не в template.tsx.
template.tsx не гарантує збереження DOM між переходами. Не зберігайте важливий стан лише в DOM-елементах, наприклад у:
значенні неконтрольованого <input>;
позиції прокручування;
відкритому <details>;
стані нативного елемента <video>.
Якщо ці дані потрібно зберегти, керуйте ними через відповідний стан або інше сховище на рівні компонента, який не перемонтовується.
'use client'Такий код буде некоректним:
import { useState, type ReactNode } from 'react';
export default function Template({
children,
}: {
children: ReactNode;
}) {
const [value, setValue] = useState('');
return children;
}Для використання React-хуків потрібно додати 'use client' на початок файлу:
'use client';keyНе потрібно додавати власний key, щоб змусити шаблон перемонтовуватися:
return <div key={someValue}>{children}</div>;Особливість template.tsx полягає саме в його поведінці як файлової конвенції Next.js. Додатковий key може ускладнити життєвий цикл компонентів і не замінює правильне розміщення template.tsx.
template.tsx — файлова конвенція App Router для повторно монтуємих обгорток.
Він приймає children і розміщується в потрібному сегменті маршруту.
На відміну від layout.tsx, шаблон не призначений для збереження стану між дочірніми навігаціями.
Client Component у template.tsx може використовувати стан, ефекти та обробники подій.
Під час нового монтування локальний стан скидається, а ефекти запускаються повторно.
Для постійного стану та UI, який має переживати навігацію, використовуйте layout.tsx.