У файловій маршрутизації App Router фізична структура каталогів напряму транслюється в загальнодоступний URL. Проте в архітектурі масштабних enterprise-застосунків вимоги до організації кодової бази часто конфліктують із вимогами до публічної структури адрес:
/login проти повнорозмірного робочого середовища /dashboard).Next.js App Router вирішує це завдання за допомогою концепції Route Groups (груп маршрутів). Огортання імені папки в круглі дужки вказує збирачу Turbopack, що цей сегмент файлової системи призначений виключно для організації модулів і застосування макетів, та повністю виключається з підсумкового шляху URL.
Коли компілятор Next.js обходить директорію app, імена папок, взяті в круглі дужки (folderName), слугують роздільниками на рівні файлової системи, але видаляються з рядка адреси при побудові таблиці маршрутів:
app/(marketing)/about/page.tsx компілюється в URL /about.app/(dashboard)/analytics/page.tsx компілюється в URL /analytics.app/(shop)/checkout/page.tsx компілюється в URL /checkout.При цьому компілятор зберігає групу у внутрішньому Route Segment Tree ID. Це дозволяє призначати групі власний незалежний layout.tsx, loading.tsx та error.tsx.
Turbopack сканує файлову структуру app/ та виявляє папки в круглих дужках (marketing), (dashboard).
Імена груп вирізаються з таблиці URL: шлях app/(marketing)/pricing/page.tsx реєструється як публічний /pricing.
Сторінка зв'язується з макетом своєї групи: pricing/page.tsx огортається в (marketing)/layout.tsx.
Під час переходу перевіряється спільний предок: якщо обидві сторінки всередині одного Root Layout — перехід м'який (SPA), інакше — hard reload.
Сервер генерує Flight stream із розміткою сторінки та макета цільової групи без зайвих клієнтських залежностей.
У проєкті створено файл app/(auth)/admin/login/page.tsx. За яким загальнодоступним URL відкриватиметься ця сторінка в браузері?
У межах єдиного застосунку різні розділи можуть вимагати різного компонування. Створюючи групи, ви ізолюєте макети без зміни структури URL:
app/
├── (marketing)/
│ ├── layout.tsx # Публічний каркас (Хедер, Промо-банер, Футер)
│ ├── page.tsx # URL: / (Головний лендінг)
│ ├── about/page.tsx # URL: /about
│ └── pricing/page.tsx # URL: /pricing
└── (dashboard)/
├── layout.tsx # Приватний каркас (Сайдбар із проєктами, меню профілю)
├── dashboard/page.tsx # URL: /dashboard
└── settings/page.tsx # URL: /settingsNext.js дозволяє повністю видалити кореневий app/layout.tsx і створити незалежні Root Layouts усередині кожної групи:
export default function MarketingRootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="uk">
<body className="marketing-theme">
{/* Чистий статичний HTML без важких клієнтських провайдерів */}
<header className="marketing-header">Маркетинг</header>
{children}
</body>
</html>
);
}import { AuthProvider } from '@/components/auth/AuthProvider';
export default function AppRootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="uk">
<body className="app-theme">
{/* Тут підключені важкі клієнтські провайдери сесії */}
<AuthProvider>
{children}
</AuthProvider>
</body>
</html>
);
}Що відбудеться в браузері, коли користувач перейде за посиланням з маркетингової сторінки /pricing (app/(marketing)/layout.tsx) на сторінку застосунку /dashboard (app/(app)/layout.tsx)?
У комерційних проєктах критично, щоб лендінг завантажувався миттєво з найвищим показником Core Web Vitals (LCP менше 1.2 с). Використання Route Groups дозволяє ізолювати клієнтські провайдери авторизації та вебсокетів виключно в групі (app), зберігаючи (marketing) повністю легковагою:
app/
├── layout.tsx (Спільний Root Shell)
├── (marketing)/
│ ├── layout.tsx ──► Zero Client JS Bundle: чистий SSR/SSG
│ └── page.tsx ──► LCP: 0.6 сек (CDN Edge Cache)
└── (workspace)/
├── layout.tsx ──► Містить AuthContext, WebSocketProvider, ReactQuery
└── editor/page.tsx ──► Інтерактивне робоче середовищеОскільки імена папок у круглих дужках виключаються з URL, створення однакових відносних шляхів у різних групах призведе до помилки збірки Turbopack:
app/(marketing)/about/page.tsx $\to$ /aboutapp/(shop)/about/page.tsx $\to$ /about
Як уникнути: Стежте за унікальністю результуючих шляхів і використовуйте правила лінтингу структури каталогів (наприклад, eslint-plugin-check-file).Якщо проєкт розбитий на кілька кореневих макетів, перехід між ними викликає повне перезавантаження сторінки (window.location). У результаті будь-які незбережені дані форм в оперативній пам'яті буде втрачено.
Як уникнути: Використовуйте єдиний app/layout.tsx для всього застосунку у 95% випадків, а групи маршрутів застосовуйте для вкладених макетів.