Parallel routes let you render multiple pages simultaneously inside a single layout, each in its own named slot. The layout receives each slot as a prop and decides where (and whether) to render it. The classic example is a dashboard where an analytics panel and a recent activity feed load independently -- if one is slow or errors out, the other still works.
The real power shows up when you combine parallel routes with intercepting routes. That combination is how you build modals that have their own URL, survive a page refresh, and close properly with the back button. Think of an Instagram-style photo overlay or a login modal that can also be accessed as a standalone page. Without parallel routes, you end up managing modal state manually and losing the URL entirely.
Master next.js parallel routes
Take the Next.js Full-Stack course with hands-on lessons and challenges.
Each slot loads independently. If the analytics query takes 3 seconds, the activity feed and main content render immediately. Each slot has its own loading.tsx, so the user sees targeted skeleton states instead of a single spinner for the whole page.
When a user refreshes the page, Next.js cannot recover slot state from the client router. Without default.tsx, unmatched slots trigger an error. Return null to hide the slot, or re-export the page component to show a sensible fallback. You also need a default.tsx for the implicit children slot.
Clicking a photo thumbnail triggers a soft navigation. Next.js intercepts the route and renders the modal slot instead of navigating away. The URL updates to /gallery/123, so the link is shareable. A hard refresh loads app/gallery/[id]/page.tsx as a full page. The (..) convention is based on route segments, not file-system levels -- @modal is a slot, not a segment, so (.) is correct here.
The layout is a Server Component, so the auth check runs on the server with zero client JS. The admin slot is never sent to the browser for regular users -- it is not just hidden with CSS, it is never rendered at all. This is a clean separation compared to a single page with role-based conditional blocks scattered everywhere.
Adding a layout inside a slot creates an independent navigation context. The tab links change what renders in @analytics without affecting the main content or other slots. The rest of the dashboard stays exactly where it is -- no full page transition, no re-fetching data in other panels.
Forgetting `default.tsx` in every slot (and for the implicit children slot) -- leading to errors on page refresh
Add a `default.tsx` to every `@slot` folder and to the route segment itself (for the children slot). Return `null` if the slot should be empty, or re-export the main page component if you want to show a fallback. Test by navigating to a sub-route and then refreshing the browser.
Thinking the `(..)` intercepting convention follows file-system paths -- and miscounting levels because of `@slot` folders
The convention is based on route segments, not file-system directories. Slot folders like `@modal` are not route segments, so they are skipped when counting levels. `(.)` inside `@modal` matches the same route level as the parent, not one level down.
Using parallel routes for simple layout composition that nested layouts already solve -- adding unnecessary complexity
If your content sections do not need independent loading states, independent error boundaries, or conditional rendering based on server-side logic, use regular nested layouts and component composition instead. Parallel routes add real file-system overhead.
Not providing a catch-all page (`[...catchAll]/page.tsx`) in modal slots, causing the modal to persist when navigating to unrelated routes
Add `@modal/[...catchAll]/page.tsx` that returns `null`. Without it, the slot stays visible during client-side navigation to routes that do not match the slot, because Next.js preserves the last matched state.
Parallel routes split a single layout into independently rendered slots using the `@folder` convention. Each slot loads, errors, and navigates on its own. The three patterns worth learning are: dashboard panels with independent streaming, modals that combine parallel slots with intercepting routes for URL-preserving overlays, and conditional rendering based on server-side auth checks. The critical implementation detail is `default.tsx` -- every slot needs one, and the implicit children slot does too, or your app breaks on hard navigation. Parallel routes are powerful, but they add file-system complexity. Reach for them when you genuinely need independent loading states, route interception for modals, or server-side conditional slot rendering. For everything else, nested layouts and component composition are simpler.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.