Dlaczego Parallel Routes i Intercepting Routes są niedoceniane w App Routerze?
Większość tutoriali Next.js App Router skupia się na layoutach, ładowaniu i Server Components. Tymczasem oraz rozwiązują problemy, z którymi frontendowcy walczą od lat: modale z własnym URL-em, panele aplikacji z niezależnymi sekcjami i płynna nawigacja bez utraty kontekstu.
Nie są nowe. Siedzą w App Routerze od wersji 13.3. Mimo to wciąż mało kto je rozumie i jeszcze mniej osób używa. Szkoda, bo gdy raz załapiesz, do czego służą, zaczynasz je widzieć wszędzie.
Parallel Routes w Next.js: wiele widoków w jednym layoucie
Czym są Parallel Routes w App Routerze?
Parallel Routes pozwalają renderować wiele stron jednocześnie w ramach jednego layoutu. Każda z nich może mieć własny stan ładowania, obsługę błędów i aktywną podstronę. Nazwane sloty nie tworzą segmentów URL. Przykładowo app/@analytics/views/page.tsx nadal odpowiada ścieżce /views, a nie /@analytics/views.
Definiujesz je za pomocą nazwanych slotów, czyli katalogów zaczynających się od @:
Jak layout korzysta ze slotów Parallel Routes?
Każdy nazwany slot jest automatycznie przekazywany do layoutu jako właściwość:
children to domyślny slot i odpowiada plikowi page.tsx w tym samym katalogu.
Podczas nawigacji klientowej Next.js zapamiętuje aktywną podstronę każdego slotu. Jeśli nowy URL nie pasuje do jednego ze slotów, framework zachowuje jego poprzedni widok. Po odświeżeniu strony nie ma jednak tego stanu w pamięci, dlatego dla niedopasowanego slotu renderowany jest default.tsx.
Warto pamiętać o jeszcze jednym ograniczeniu. Na tym samym poziomie segmentu nie można łączyć osobnych slotów statycznych i dynamicznych. Jeśli jeden slot staje się dynamiczny, wszystkie sloty na tym poziomie są traktowane jako dynamiczne.
Niezależne ładowanie i obsługa błędów w slotach
Każdy slot może mieć własny loading.tsx i error.tsx:
Jeśli slot @analytics ładuje dane z wolnego API, reszta panelu jest widoczna natychmiast. Jeśli ten slot wyrzuci błąd, error.tsx wyświetli się tylko w jego obszarze, bez wpływu na @notifications czy children.
Obowiązuje przy tym standardowa zasada Error Boundaries w App Routerze. error.tsx nie przechwytuje błędów z layout.tsx znajdującego się na tym samym poziomie, ponieważ granica błędu opakowuje dopiero jego zawartość potomną. Błąd takiego layoutu musi obsłużyć error.tsx w segmencie nadrzędnym.
Warunkowe renderowanie slotów w App Routerze
Parallel Routes doskonale nadają się do renderowania treści w zależności od stanu, na przykład roli użytkownika:
Ten warunek steruje widokiem, ale nie powinien być jedynym zabezpieczeniem dostępu. Layouty są zachowywane podczas nawigacji klientowej i nie muszą ponownie sprawdzać sesji przy każdej zmianie trasy. Autoryzację danych i operacji wykonuj także blisko źródła danych, a każdą Server Action i każdy Route Handler traktuj jak osobny punkt wejścia wymagający kontroli uprawnień.
Plik default.tsx jako wariant zapasowy dla niedopasowanych slotów
Po pełnym załadowaniu strony Next.js nie może odtworzyć zapamiętanego stanu slotu, jeśli ten nie pasuje do bieżącego URL-a. Do tego służy wariant zapasowy w pliku default.tsx:
Od Next.js 16 każdy nazwany slot wymaga jawnego default.tsx, a jego brak zatrzymuje kompilację. children jest slotem domyślnym. Gdy również może pozostać bez dopasowanej strony po pełnym załadowaniu, dodaj default.tsx obok layoutu. Bez niego taka trasa zakończy się stroną 404.
Intercepting Routes w Next.js: modale z prawdziwym URL-em
Problem UX, który rozwiązują Intercepting Routes
Wyobraź sobie galerię zdjęć. Klikasz miniaturkę i otwiera się modal z powiększonym zdjęciem. URL w przeglądarce zmienia się na /photo/123. Gdy wkleisz ten URL bezpośrednio, widzisz pełną stronę zdjęcia, nie modal.
To zachowanie znane z Instagrama, Pinteresta czy Dribbble. Bez Intercepting Routes implementacja wymaga skomplikowanego zarządzania stanem i historią przeglądarki. Z nimi wynika z routingu.
Konwencja nazewnictwa Intercepting Routes
Intercepting Routes używają specjalnej notacji w nazwach katalogów:
| Notacja | Znaczenie |
|---|---|
(.) | Przechwytuje trasę na tym samym poziomie |
(..) | Przechwytuje trasę jeden poziom wyżej |
(..)(..) | Przechwytuje trasę dwa poziomy wyżej |
(...) | Przechwytuje trasę od katalogu app/ |
Poziomy są liczone według segmentów URL, a nie katalogów w systemie plików. Katalogi nazwanych slotów, takie jak @modal, nie są segmentami i nie zwiększają liczby wymaganych (..). To dlatego @modal/(.)photo może przechwycić główną trasę /photo, mimo że w drzewie plików wygląda na głębiej zagnieżdżoną.
Przykład Intercepting Routes: galeria z modalem
Struktura plików:
Layout z slotem na modal:
Domyślny wariant zapasowy jest pusty, gdy modal nie jest aktywny:
Galeria musi otwierać zdjęcia przez Link. Dzięki temu Next.js wykonuje nawigację klientową i może przechwycić trasę:
Samą obsługę dialogu warto oddzielić od zawartości strony. Natywny element dialog zapewnia obsługę klawisza Escape, przenosi fokus do okna i oznacza tło jako nieaktywne:
Przechwycona strona może pozostać Server Componentem. Tylko powłoka modala wymaga kodu klientowego:
Po nawigacji klientowej niedopasowany slot zachowuje ostatni aktywny widok. Trasa catch-all jawnie go czyści, gdy użytkownik przejdzie z otwartego modala pod inny adres:
Pełna strona zdjęcia po pełnym przeładowaniu lub wejściu z bezpośredniego URL:
W rzeczywistej aplikacji obie wersje powinny pobierać ten sam rekord i korzystać ze wspólnego komponentu treści. Jeśli zdjęcie nie istnieje, wywołaj notFound() zarówno w pełnej stronie, jak i w przechwyconej wersji. Zapobiega to rozbieżnościom między modalem a adresem indeksowanym przez wyszukiwarkę. Domenę example.com w linku kanonicznym zastąp adresem produkcyjnym.
Jak Intercepting Routes działają w praktyce?
- Użytkownik jest na
/galleryi klika zdjęcie opakowane wLink. Next.js przechwytuje nawigację do/photo/123i renderuje wersję modalną z@modal/(.)photo/[id]/page.tsx. - URL zmienia się na
/photo/123, ale kontekst galerii pozostaje w tle. - Zamknięcie dialogu wywołuje
router.back()i przywraca galerię. Nawigacja do innej strony trafia do[...catchAll], który czyści slot. - Bezpośrednie wejście na
/photo/123lub odświeżenie strony renderuje pełną stronę zphoto/[id]/page.tsx.
Parallel Routes i Intercepting Routes jako duet w App Routerze
Oba te mechanizmy najczęściej wykorzystuje się w parze, gdzie modal oparty na interceptowanych ścieżkach jest w rzeczywistości nazwanym slotem działającym w ramach routingu równoległego. Pozwala to na przykład na zbudowanie panelu aplikacji z wieloma sekcjami, który jednocześnie potrafi płynnie przechwytywać nawigację do szczegółów. Załóżmy, że kliknięcie na wybrane zamówienie natychmiast otwiera modal ze szczegółowymi informacjami, pozwalając użytkownikowi na komfortową pracę bez opuszczania aktualnego widoku listy.
Typowe przypadki użycia Parallel Routes i Intercepting Routes
E-commerce z szybkim podglądem produktu w modalu i pełną stroną produktu po bezpośrednim wejściu
Panel aplikacji z niezależnymi sekcjami, osobnym stanem ładowania i granicą błędu
Galeria / portfolio z powiększonym podglądem i URL-em do udostępniania
Formularze krok po kroku, w których każdy krok ma własny URL, ale layout się nie przeładowuje
Porównywarka z dwoma slotami dla niezależnie wybieranych produktów
Najczęstsze problemy z Parallel Routes i Intercepting Routes
„Slot nie renderuje się po nawigacji”
Najpierw rozróżnij nawigację klientową od pełnego załadowania strony. Podczas nawigacji po stronie klienta Next.js zachowuje poprzedni aktywny widok dla niedopasowanego slotu, natomiast po odświeżeniu korzysta z pliku default.tsx. Warto pamiętać, że w Next.js brak tego pliku w nazwanym slocie skutkuje błędem kompilacji.
„Modal pozostaje widoczny po przejściu na inną stronę”
Dodaj trasę [...catchAll]/page.tsx zwracającą null wewnątrz slotu modala, ponieważ sam plik default.tsx nie zamknie aktywnego slotu podczas nawigacji klientowej.
„Przycisk wstecz nie zamyka modala”
Upewnij się, że otwarcie nastąpiło przez Link, modal jest renderowany w nazwanym slocie, a jego zamknięcie wywołuje router.back(). Wtedy historia zawiera stronę, nad którą otwarto modal, a przycisk Dalej może ponownie go wyświetlić.
„Odświeżenie strony pokazuje modal zamiast pełnej strony”
Konwencja kropki w nawiasie (.) musi ściśle odpowiadać położeniu docelowej trasy w strukturze segmentów URL, nie wliczając przy tym katalogu @modal, ponieważ nazwany slot nie stanowi fizycznego segmentu ścieżki. Warto pamiętać, że przy pełnym odświeżeniu strony przechwycona trasa przestaje działać ze względu na brak nawigacji po stronie klienta, przez co Next.js automatycznie renderuje oryginalny plik odpowiedzialny za widok szczegółów
