Групи маршрутів у Next.js
Групи маршрутів (route groups) у Next.js — папки в дужках, які структурують проект без будь-якого впливу на URL-шлях.
Теорія
TL;DR
- Синтаксис: назва папки в дужках, наприклад
(marketing)або(auth) - Назва папки ніколи не з'являється в URL
- Кожна група може мати власний
layout.tsx,loading.tsxіerror.tsx - Головне застосування: різні лейаути для публічних і авторизованих сторінок
- Якщо дві групи дають однаковий URL — Next.js видасть помилку під час білду
Короткий приклад
app/
(marketing)/
about/page.tsx → /about
pricing/page.tsx → /pricing
(dashboard)/
settings/page.tsx → /settings
analytics/page.tsx → /analyticsПапки (marketing) і (dashboard) роутер повністю ігнорує. URL-адреси: /about, /pricing, /settings, /analytics. Назви груп зникають.
Чим групи відрізняються від звичайних папок
Звичайна папка app/marketing/about/page.tsx дає URL /marketing/about. Папка з групою app/(marketing)/about/page.tsx дає просто /about. Одна відмінність, але з конкретним наслідком: до кожної групи можна прикріпити окремий layout.tsx. Тоді /about і /pricing матимуть лейаут з публічним navbar і footer, а /settings і /analytics — лейаут із sidebar і перевіркою авторизації. Жодного зайвого сегмента в URL.
Як роутер обробляє назви груп
Next.js читає дужки як сигнал виключити папку з генерації URL. Папка все одно існує на диску і може містити будь-які спеціальні файли: layout.tsx, loading.tsx, error.tsx, template.tsx. Роутер сприймає групу як область видимості, а не як сегмент шляху.
Коли використовувати
- Публічний сайт і особистий кабінет з різними лейаутами
- Сторінки авторизації (
/login,/register,/forgot) під спільним wrapper-ом - Адмінка з окремим кореневим лейаутом і власними тегами
<html>та<body> - Велика кодова база, організована за функціями, без забруднення URL
Типові помилки
Конфліктні шляхи в різних групах:
// Обидва дають /about — Next.js видасть помилку під час білду
app/(marketing)/about/page.tsx
app/(info)/about/page.tsxДужки ховають назву папки від URL, але не від логіки роутингу. Я бачив, як цей конфлікт спливає на код-рев'ю, коли два розробники в один день додають сторінки до різних груп. Варто тримати плоский список усіх URL-шляхів при плануванні структури.
Помилкове відчуття захисту від конфліктів:
// Назви груп різні, але маршрути все одно конфліктують
app/(site)/contact/page.tsx → /contact
app/(support)/contact/page.tsx → /contact ❌Обидва шляхи ведуть до /contact. Назва групи не має значення для кінцевого URL.
Сторінки поза групами при кількох кореневих лейаутах:
Якщо розбити додаток на (main) і (admin) з окремими кореневими лейаутами і видалити app/layout.tsx, кожен маршрут верхнього рівня має бути всередині однієї з груп. Сторінка app/about/page.tsx поза групами не матиме кореневого лейаута.
Запитання на співбесіді
Q: Чи можна вкладати групи маршрутів одна в одну?
A: Так. app/(marketing)/(campaigns)/summer/page.tsx — валідний шлях, і жодна назва групи не з'являється в URL. Більшість команд вдається до вкладеності тільки коли підсекції потрібен власний спільний лейаут.
Q: Як групи маршрутів впливають на middleware?
A: Ніяк. Middleware зіставляє реальний URL-шлях, а не структуру файлів. Матчер /dashboard/:path* працює однаково незалежно від того, лежать файли в app/dashboard/ чи в app/(auth)/dashboard/.
Q: Яка різниця між лейаутом групи і звичайним вкладеним лейаутом?
A: Звичайний вкладений лейаут додає сегмент до URL. Лейаут групи обгортає маршрути так само, але без додавання сегмента. Поведінка обгортання ідентична.
Q: Якщо дві групи-сусіди мають layout.tsx, вони накладаються?
A: Вони обидві накладаються на кореневий app/layout.tsx, але між собою не взаємодіють. Кожен маршрут належить рівно одній групі.
Приклади
Окремі лейаути для публічної частини і дашборду
Маркетинговий сайт і дашборд живуть в одному Next.js-проекті, але потребують різного оформлення сторінок. Групи маршрутів вирішують це без зміни URL.
// app/(marketing)/layout.tsx
export default function MarketingLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<PublicNavbar />
<main>{children}</main>
<Footer />
</div>
);
}
// app/(dashboard)/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
);
}/about і /pricing отримують navbar і footer. /settings і /analytics — sidebar. Жодна назва групи не потрапляє в URL.
Кілька кореневих лейаутів
Коли адмінці потрібна інша структура <html>, інші шрифти або суворіші правила CSP, можна видалити app/layout.tsx і дати кожній групі власний кореневий лейаут.
app/
(main)/
layout.tsx ← кореневий лейаут з аналітикою і open graph тегами
page.tsx
about/page.tsx
(admin)/
layout.tsx ← спрощений кореневий лейаут без сторонніх скриптів
dashboard/page.tsx
users/page.tsxКожен layout.tsx тут — повноцінний HTML-документ з <html>, <head> і <body>. Next.js рендерить кожну секцію зі своєю оболонкою. Якщо обрати цей підхід, кожна сторінка має лежати всередині однієї з груп — запасного варіанту немає.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.