Parallel Routes and Intercepting Routes in the Next.js App Router: Field Notes on the Photo-Modal Pattern

2026年9月1日2 次浏览来源:Dev.to阅读原文

Headline: Parallel routes let a Next.js App Router layout render more than one page at the same URL through named folders, and intercepting routes — the , , , and folder conventions — let a link swap in a different component for that slot without changing the URL.

Together they're how an Instagram-style photo modal stays shareable: the same URL renders a modal on a soft navigation and a full page on a hard refresh, and is the seam between the two.

Key takeaways A parallel route is a named slot — a folder prefixed with , like — that Next.js renders as a prop into the nearest , alongside the implicit slot.

Slot folders don't add a segment to the URL.

Intercepting route conventions — , , , — match a route relative to the file system, not the URL, so a link clicked inside the feed can render as a modal without any folder named ever appearing in the URL. is what Next.js renders for a slot when the current URL doesn't match anything inside that slot.

Skip it and a hard navigation to a route that doesn't fill every slot 404s.

A hard refresh or a shared link to is not supposed to show the modal — it's supposed to render as an ordinary full page.

That fallback is the entire point of the pattern, not a bug to route around.

Each slot is its own subtree with its own and , so a modal can suspend and stream independently of the page rendering behind it.

What problem do parallel routes actually solve?

A parallel route renders more than one page in the same layout at the same time, each addressed by a named slot instead of a URL segment.

I first reached for this on a photo grid: clicking a thumbnail should open a lightbox without leaving the grid, but the lightbox also needed its own shareable URL so a link to one photo would open directly on a full page.

A client-side modal driven by a boolean gets the first half for free and can't do the second half at all — there's no URL for a piece of state that lives in .

The folder convention is a name prefixed with .

Next.js passes each slot's matched content into the nearest layout as a prop named after the folder, alongside the implicit slot every layout already receives.

Nothing here is modal-specific yet — a layout with two slots is just a layout that renders two independent subtrees.

I've since used the same mechanism for a dashboard with and panes that navigate independently: clicking a link inside re-renders only that slot, and keeps whatever it was showing.

How does the (.) convention decide what gets intercepted?

The dot-segment conventions match a target route relative to where the intercepting file sits in the file system, not relative to the current URL: Convention Matches A route at the same folder level A route one folder level above A route two folder levels above A route from the app root intercepts a client-side navigation to when that navigation is triggered from a route sitting at the same level as the folder.

Follow a from that page and Next.js renders the intercepting component into the slot instead of swapping out the whole tree.

Open as a fresh page load — refresh, paste the URL, click a link from an external site — and Next.js resolves the ordinary way.

The interception only fires on a client-side transition; it was never meant to change what a full page load returns.

Why does default.tsx exist, and what happens if I skip it?

The first time I skipped it, everything worked in dev until I hit refresh on a route with the modal open and got a

404.

A slot without a matching segment for the current URL needs something to render, and on a full page load there's no prior client state to fall back to — Next.js has to render the slot from scratch. is that fallback.

Client-side navigation is more forgiving: if a slot has no and the new URL doesn't match anything inside it, Next.js keeps rendering whatever that slot last showed rather than unmounting it.

That's genuinely useful for the dashboard-tabs case — switching tabs in shouldn't reset — but it means a missing can hide itself in every manual click-through test and only surface on a hard reload, which is exactly the path a shared link takes.

Why does refreshing the modal route show the full page instead of the modal?

Because it's supposed to.

That was the part I fought before I read the pattern correctly: I wanted the modal to somehow survive a refresh, and the actual design is that it shouldn't. is a complete, independent page — same content, no modal chrome, reachable without a single line of client JavaScript executing first.

That's what makes the URL genuinely shareable and indexable: a search engine or a link preview bot that doesn't run your client-side router gets the real page, not an empty shell waiting for JavaScript to open a dialog.

How do I close the modal and get back to where I came from?

The modal component is a Client Component that calls from , which reverses the soft navigation that opened it and lets the intercepted slot fall back to its .

The gap I found in production: a visitor who opens directly in a new tab from a shared link has no history entry to go back to, so either does nothing or leaves the app.

I ended up rendering an explicit close link with a real back to the gallery alongside the overlay handler, so closing the modal never depends on history existing.

Client-state modal vs. intercepting-route modal — what's the actual trade-off?

Boolean-state modal Intercepting route modal Own URL No Yes Shareable / shows on refresh No Yes — renders as a full page Needs client JS to render content at all Yes No, on direct navigation Setup cost One A slot, an intercepting folder, a fallback route, For a dialog that's genuinely disposable UI — a confirmation prompt, a settings panel — the boolean is still the right tool.

Reach for the routed version specifically when the content behind the modal deserves its own URL.

FAQ Q: Do parallel route slot folders like show up in the URL?

A: No.

The prefix marks a folder as a slot rather than a route segment, so it's invisible to the URL and only affects which prop of the layout receives its content.

Q: What happens if someone navigates straight to instead of clicking a thumbnail in the feed?

A: Next.js renders as a normal full page.

The interception only happens on a client-side transition from a matching route, by design.

Q: Can a parallel route slot have its own ?

A: Yes.

Each slot is an independent subtree and can define its own , , and , so a modal can suspend on its own data fetch without blocking the page behind it.

Q: Why doesn't close the modal for someone who opened the link directly?

A: There's no history entry to go back to when a route is the first thing loaded in a tab.

Pair the close handler with an explicit link to a real fallback route instead of relying on history alone.

Q: Can I combine with a parallel route slot defined several levels up the tree?

A: Yes — the dot count is independent of where the folder lives; it only describes how many folder levels above the intercepting file's own location the target route sits.

Originally published on devya.dev.

Also on eng-ahmed.com.

Built by Devya Solutions.

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools