// app/layout.tsx
import React from 'react'
interface LayoutProps {
children: React.ReactNode
modal: React.ReactNode
}
export default function RootLayout({
children,
modal,
}: LayoutProps) {
return (
<html lang="ro">
<body>
<main className="container mx-auto">
{children}
</main>
{/* Aici se va injecta modalul când ruta e interceptată */}
{modal}
</body>
</html>
)
}Am implementat recent chestia asta la un proiect cu vreo 12k useri activi unde aveam o galerie mare de imagini. Clientul voia neapărat ca atunci când dai click pe o poză să se deschidă un modal rapid, fără reload, dar dacă trimiți linkul unui prieten, ăla să vadă poza direct pe pagina ei dedicată, full-screen. Dacă folosești doar state (isOpen, setIsOpen), pierzi complet avantajul URL-ului unic și al SEO-ului.
Next.js vine cu o soluție elegantă, dar extrem de contraintuitivă la prima vedere: rutele paralele (@modal) combinate cu rutele interceptate ((.)photos/[id]).
Structura de foldere care dă dureri de cap
Chestia asta e cel mai greu de înțeles dacă doar citești documentația lor oficială. Ai nevoie de o structură exactă, altfel Next.js pur și simplu ignoră interceptarea.
Arată cam așa:
app/
├── @modal/
│ ├── (.)photos/
│ │ └── [id]/
│ │ └── page.tsx <-- Aici e codul pentru modal
│ └── default.tsx <-- Obligatoriu! Returnează null
├── photos/
│ └── [id]/
│ └── page.tsx <-- Pagina normală (full screen)
├── layout.tsx <-- Primește @modal ca prop
└── page.tsx <-- Pagina de start cu galeria
Directiva (.) îi spune lui Next.js: "Dacă userul navighează aici din interiorul aplicației (soft navigation), interceptează ruta asta și randează folderul curent în slotul paralelei". Dacă dă refresh (hard navigation), Next.js va ignora interceptarea și va încărca direct /photos/[id] din folderul normal.
De ce e obligatoriu default.tsx?
Aici am pierdut eu vreo trei ore la primul proiect. Când ești pe pagina principală (/), Next.js încearcă să randeze slotul @modal. Dar nu are ce randa acolo pentru că nu ai interceptat nicio rută încă. Dacă nu ai un fișier default.tsx care să returneze null în interiorul folderului @modal, Next.js va arunca o eroare 404 pe toată pagina la refresh.
Practic, default.tsx servește ca fallback atunci când ruta paralelă nu este activă.
Trade-off-ul sincer: de ce e o sabie cu două tăișuri
Hai să fim realiști. UX-ul final este absolut genial. Pagina se simte ca o aplicație nativă, tranziția e instantă și URL-ul se schimbă frumos în browser. Am măsurat un boost de 15% în engagement pe poze doar pentru că userilor le era mult mai ușor să dea back fără să piardă scroll-ul din spate.
Dar din punct de vedere al developmentului, e destul de greu de debuguit.
În primul rând, ai duplicare de logică vizuală. Trebuie să ai componenta de detalii foto care să poată fi randată și ca modal (în interiorul unui dialog), și ca pagină de sine stătătoare. Dacă nu ești atent cum îți organizezi componentele, ajungi să scrii același fetch de date în două locuri diferite.
În al doilea rând, caching-ul din Next.js o mai ia razna la navigări complexe înapoi-înainte. Uneori te trezești că modalul rămâne blocat pe ecran sau că ruta de sub el nu se mai actualizează corect.
Voi ați încercat abordarea asta în producție sau preferați să rămâneți la clasicele modaluri controlate prin query params (?photoId=123)?