Сучасні високонавантажені вебдодатки вимагають складних і динамічних сценаріїв відображення інтерфейсу:
loading.tsx) та ізольовано обробляти збої (error.tsx), не порушуючи роботу сусідніх панелей./photo/123). При цьому прямий захід за цим же посиланням або оновлення сторінки через F5 повинні відкривати повноцінну окрему сторінку, а не модалку.Next.js App Router вирішує ці завдання на рівні ядра за допомогою паралельних маршрутів (Parallel Routes) та перехоплюючих маршрутів (Intercepting Routes).
@slot)#Паралельні маршрути створюються з використанням папок із префіксом @ (слотів). Імена слотів не потрапляють у публічний URL (app/dashboard/@analytics компілюється в URL /dashboard), а передаються як іменовані React-пропси в компонент layout.tsx відповідного рівня.
Основний файл page.tsx неявно є слотом за замовчуванням children (еквівалентний @children). Кожен паралельний слот є незалежною гілкою React-дерева зі своїм життєвим циклом, межею Suspense та кешем.
(.), (..))#Перехоплюючі маршрути дозволяють замапити клієнтський перехід за посиланням на альтернативний компонент, зберігаючи контекст поточного екрана:
(.) — перехоплення маршруту на тому ж рівні сегмента.(..) — перехоплення маршруту на один сегмент вище.(..)(..) — перехоплення на два сегменти вище.(...) — перехоплення від самого кореня директорії app.Важливо: Папки паралельних слотів (
@slot) і групи маршрутів ((group)) не є сегментами URL і повністю ігноруються при підрахунку рівнів для(..).
Користувач перебуває на /feed і клікає <Link href='/photo/42' scroll={false}>.
Turbopack визначає перехоплювач app/feed/@modal/(..)photo/[id]/page.tsx замість стандартної сторінки.
Layout рендерить фоновий children (/feed) та впроваджує модалку в слот @modal. URL змінюється на /photo/42.
Користувач натискає F5 або переходить за посиланням напряму з месенджера.
Сервер рендерить повноекранний app/photo/[id]/page.tsx. Фоновий слот використовує default.tsx (null), уникаючи помилки 404.
Що станеться, якщо користувач натисне F5 на сторінці з відкритою через @modal карткою /photo/42, але в директорії слота відсутній файл default.tsx?
Паралельні слоти передаються в layout.tsx як іменовані React-пропси:
interface DashboardLayoutProps {
children: React.ReactNode;
analytics: React.ReactNode; // Слот app/dashboard/@analytics
notifications: React.ReactNode; // Слот app/dashboard/@notifications
}
export default function DashboardLayout({
children,
analytics,
notifications,
}: DashboardLayoutProps) {
return (
<div className="p-6 space-y-6">
<main>{children}</main>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<section className="border border-border p-4 rounded-lg">
{analytics}
</section>
<section className="border border-border p-4 rounded-lg">
{notifications}
</section>
</div>
</div>
);
}Файл default.tsx визначає, що відображати в паралельному слоті, коли поточний URL не збігається з жодним маршрутом усередині цього слота:
// Якщо модальне вікно не активне, повертаємо null для приховування слота
export default function DefaultModalSlot() {
return null;
}У вас є структура app/feed/@modal/(..)photo/[id]/page.tsx. Скільки рівнів URL перехоплює оператор (..) у цьому випадку?
'use client';
import { useRouter } from 'next/navigation';
import { use } from 'react';
interface PhotoModalProps {
params: Promise<{ id: string }>;
}
export default function PhotoModal({ params }: PhotoModalProps) {
const router = useRouter();
const { id } = use(params);
return (
<div className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4">
<div className="bg-bg-surface border border-border rounded-xl max-w-lg w-full p-6 space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-bold">Фото #{id}</h3>
{/* router.back() відновлює попередній URL без модалки */}
<button
onClick={() => router.back()}
className="text-text-muted hover:text-text-primary text-sm px-2 py-1 rounded bg-bg-elevated"
>
Закрити ✕
</button>
</div>
<div className="aspect-video bg-black/40 rounded-lg flex items-center justify-center font-mono text-xs text-text-muted">
[Перегляд фотографії {id}]
</div>
</div>
</div>
);
}Якщо ви використовуєте паралельні маршрути (@slot), відсутність файлу default.tsx у корені слота призведе до того, що під час першого ж оновлення сторінки клавішею F5 весь додаток впаде з помилкою 404.
Як уникнути: Завжди створюйте файл default.tsx (який хоча б повертає return null;) у кожному оголошеному слоті.
Розробники часто пишуть (..)(..)photo, помилково вважаючи, що @modal вважається за окремий сегмент URL. У результаті перехоплювач не спрацьовує.
Як уникнути: Запам'ятайте: папки зі знаком @ та круглими дужками () прозорі для URL-маршрутизатора. Для виходу з app/feed/@modal на рівень app/ потрібен рівно один оператор (..).
Під час переходу за посиланням <Link href="/photo/123"> браузер за замовчуванням прокручує сторінку в самий верх, збиваючи положення фонового списку.
Як уникнути: Завжди вказуйте прапорець scroll={false} під час виклику посилань, що відкривають контекстні модальні вікна: <Link href="/photo/123" scroll={false}>.