Back to docs
Recipe: Modal-as-route pattern
Intercepting routes so a detail view opens as a modal overlay while keeping its own shareable URL.
The problem
You want a photo grid where clicking a photo opens a detail modal, but the modal must have its own route (/photos/42) so users can share or refresh it.
Folder layout
app/
photos/
page.tsx ← grid (server component)
layout.tsx ← wraps children in modal shell
[id]/
page.tsx ← full detail page (direct visit)
@modal/
(.)photos/
[id]/
page.tsx ← intercepted modal viewKey mechanics
- Parallel route
@modalrenders alongsidechildren. - Intercepting route
(.)photoscatches navigation from the grid and swaps in the modal. - Direct visits or refreshes hit
photos/[id]/page.tsx— the full standalone page.
layout.tsx sketch
export default function PhotosLayout({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<>
{children}
{modal}
</>
);
}Caveats
The modal slot is always rendered — guard it with a null check inside the intercepted page. Use router.back() to dismiss so the grid scroll position is preserved.
Next step: Recipe: Parallel routes deep-dive