eduardweb.
App RouterAvansat#nextjs#ux#app-router#routing

Cum folosești Parallel și Intercepting Routes pentru un modal pe /photos/[id]

De Dan Ciobanu, 10 iun. 2026 · 20 vizualizări · 3 like-uri

Postat 10 iun. 2026
typescript
// app/gallery/layout.tsx
export default function GalleryLayout({
  children,
  modal,
}: {
  children: React.ReactNode;
  modal: React.ReactNode;
}) {
  return (
    <div className="relative min-h-screen">
      <main className="container mx-auto">{children}</main>
      {/* Slotul pentru modal va fi randat doar când ruta e interceptată */}
      {modal}
    </div>
  );
}

Am implementat pattern-ul ăsta anul trecut pentru un site de prezentare cu peste 12.000 de imagini și inițial mi-am prins urechile în structura de foldere din App Router. Vă scriu ghidul ăsta scurt ca să nu pierdeți două zile căutând de ce se încarcă pagina complet în loc să se deschidă modalul. Câștigați timp și un UX brici.

De ce ne legăm la cap cu asta?

Tradițional, un modal era doar o stare de React (isOpen). Dar dacă vrei ca userul să poată da copy-paste la URL-ul din modal și să-l trimită unui prieten, ai nevoie de o rută reală. Dacă pui doar un link simplu spre /photos/42, Next.js va face hard navigation și va randa direct pagina de detaliu, distrugând contextul listei din spate.

Cu Parallel Routes (@modal) și Intercepting Routes ((.)photos), când userul dă click pe o poză din galerie, Next.js interceptează navigarea în client. Randează modalul peste pagina curentă (păstrând grila de poze intactă în fundal). Dacă dă refresh (F5), se încarcă direct pagina de detaliu /photos/42. Super elegant, dar structura de foldere arată ca o glumă proastă cu regex.

Structura de foldere (Aici e tot secretul)

Trebuie să mapăm corect sloturile. Să zicem că avem galeria în /gallery. Structura trebuie să arate fix așa:

app/
├── gallery/
│   ├── @modal/
│   │   ├── (.)photos/
│   │   │   └── [id]/
│   │   │       └── page.tsx  <-- Modalul interceptat
│   │   └── default.tsx       <-- Necesar când modalul e închis
│   ├── layout.tsx            <-- Primește children și modal
│   └── page.tsx              <-- Grila de poze
└── photos/
    └── [id]/
        └── page.tsx          <-- Pagina normală (fallback la F5)

Segmentul (.) înseamnă "interceptează la același nivel". Dacă ruta /photos ar fi fost cu un nivel mai sus în ierarhie față de @modal, foloseam (..) ca în path-urile de terminal.

Nu uita de default.tsx. Dacă uiți să-l pui în rădăcina folderului @modal, Next.js va returna 404 la navigări hard pe pagini adiacente, pentru că nu știe ce să randeze în slotul de modal când acesta trebuie să fie inactiv.

Trade-off-uri pe bune

Să fim sinceri. Soluția asta merge excelent pentru vizualizare de date (read-only views, detalii produs, galerii foto). Am economisit cam 30% din timpul de navigare pentru că nu mai reîncărcăm tot layout-ul din spate de fiecare dată.

Însă, e un mic dezastru dacă ai formulare complexe în modal și vrei să schimbi starea din spate (de exemplu, să ștergi poza și să se actualizeze instant grila din background). router.refresh() ajută, dar uneori introduce un mic layout shift vizual care te scoate din minți. De asemenea, dacă ai animații de exit pe modal, devine foarte greu să le sincronizezi cu distrugerea slotului de către Next.js.

Dacă aveți de făcut un dashboard simplu sau un portofoliu unde userul doar cască ochii la imagini, folosiți-l cu încredere. Pentru aplicații grele de tip CRUD, mai bine rămâneți la query params (?modal=true) și gestionare manuală. Voi ce soluție folosiți pentru modale cu URL partajabil în App Router?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.