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

Cum faci un modal de tip Instagram cu Parallel și Intercepting Routes în Next.js

De Cristian Barbu, 8 iun. 2026 · 1 vizualizări · 3 like-uri

Postat acum 1 zi
javascript
// app/layout.jsx
export default function Layout({
  children,
  modal
}) {
  return (
    <html lang="ro">
      <body>
        <main className="container">
          {children}
        </main>
        {/* Aici se va randa modalul când ruta este interceptată */}
        {modal}
      </body>
    </html>
  );
}

// app/@modal/default.jsx
export default function Default() {
  // Returnăm null pentru a nu afișa nimic când nu avem o rută interceptată
  return null;
}

Am avut de implementat recent chestia asta pe un proiect cu vreo 12.000 de imagini încărcate de utilizatori. Clientul voia comportamentul clasic de pe Instagram sau Pinterest: dai click pe o poză, se deschide un modal superb cu detalii, URL-ul din bară se schimbă în /photos/123, dar fundalul (feed-ul de poze) rămâne exact unde era, fără scroll reset sau reîncărcare. În schimb, dacă utilizatorul dă refresh la acel URL sau trimite link-ul unui prieten, ruta trebuie să randeze o pagină de sine stătătoare, cu detaliile pozei pe tot ecranul.

În Next.js App Router, poți rezolva asta curat, fără să te chinui cu state management global sau query params urâți, folosind Parallel Routes și Intercepting Routes în tandem. Am economisit vreo 40% din codul de state management pe care îl scriam înainte doar pentru a gestiona deschiderea, închiderea și animațiile unui modal.

Structura de foldere e tot secretul

Next.js folosește convenții stricte de denumire pentru a prinde aceste rute speciale. Ai nevoie de slotul @modal (pentru parallel route) și de folderul (.)photos (pentru intercepting route). Paranteza cu punct (.) îi spune framework-ului să intercepteze ruta /photos doar atunci când navigarea se face prin client-side transition (adică atunci când dai click pe un <Link>).

Structura arată cam așa în proiectul meu:

app/
├── @modal/
│   ├── (.)photos/
│   │   └── [id]/
│   │       └── page.jsx  <-- Modalul care se deschide la click
│   └── default.jsx       <-- Returnează null (esențial!)
├── photos/
│   └── [id]/
│       └── page.jsx      <-- Pagina clasică (la refresh sau share link)
├── layout.jsx            <-- Primește props-ul @modal
└── page.jsx              <-- Feed-ul de poze

Cum legăm totul în Layout

În layout.jsx, Next.js ne injectează automat slotul @modal ca prop, pe lângă clasicul children. Trebuie doar să le randăm pe amândouă în același loc.

Un detaliu extrem de important peste care am dat cu capul: ai absolută nevoie de fișierul default.jsx în rădăcina lui @modal. De ce? Pentru că atunci când ești pe pagina principală și nu ai niciun modal activat, Next.js trebuie să știe ce să randeze în acel slot. Dacă nu pui default.jsx care să returneze null, te vei trezi cu erori de tip 404 sau pagini albe la navigare.

Trade-off-uri de care trebuie să fii conștient

Deși UX-ul final este impecabil, arhitectura asta vine cu o bătaie de cap destul de mare la debugging.

În primul rând, devine complicat dacă vrei să transmiți date proaspete de la pagina de fundal la modal fără să faci un fetch nou în rețea. De obicei, ești obligat să re-obții datele pozei în interiorul modalului prin server components sau să te bazezi pe un cache SWR/React Query la nivel de client.

În al doilea rând, controlul animațiilor de închidere (cum ar fi un fade-out discret) este destul de anevoios când utilizatorul apasă tasta Back în browser. Next.js va distruge instant ruta interceptată, iar modalul va dispărea brusc, fără tranziție, dacă nu implementezi un hack cu stări locale pentru a întârzia unmount-ul.

La proiecte mari, unde experiența userului contează enorm, merită efortul 100%. Voi cum gestionați modalele de genul ăsta? Mergeți pe rute interceptate sau preferați varianta clasică cu query params gen ?photoId=123?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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