{"slug": "parallel-routes-in-next-js-app-router-rendering-multiple-pages-in-one-layout", "title": "Parallel Routes in Next.js App Router — Rendering Multiple Pages in One Layout", "summary": "Parallel Routes in Next.js App Router allow rendering multiple pages simultaneously within the same layout, enabling independently navigable sections like a dashboard with a sidebar and main content. The feature uses named slots (folders prefixed with @) and default.tsx files to handle navigation without 404 errors. A common use case is opening a modal without losing the underlying page, as demonstrated in Pixova's AI character generator.", "body_md": "Parallel Routes let you render multiple pages simultaneously within the same layout. Instead of navigating away from a page to show another one, you can display both at the same time — a dashboard with independently navigable sections, a main content area alongside a sidebar that changes with its own navigation, or a feed with an openable detail panel that doesn't replace the feed.\n\nThis is one of the more powerful App Router features and one of the more confusing to set up initially. Here's the complete pattern, including the design approach used for complex multi-panel interfaces like [the generation tool at Pixova](https://pixova.io/blog/free-ai-character-generator).\n\nParallel routes use named slots: special folders prefixed with `@`\n\nthat define independent rendering areas within a layout.\n\n```\napp/\n├── layout.tsx              ← Receives slot props\n├── page.tsx                ← Default slot content\n├── @sidebar/\n│   ├── page.tsx            ← Sidebar default\n│   └── settings/\n│       └── page.tsx        ← Sidebar settings view\n└── @modal/\n    ├── page.tsx            ← Modal default (null)\n    └── photo/[id]/\n        └── page.tsx        ← Photo modal\n```\n\nThe layout receives each slot as a prop:\n\n```\n// app/layout.tsx\nexport default function Layout({\n  children,\n  sidebar,\n  modal,\n}: {\n  children: React.ReactNode;\n  sidebar: React.ReactNode;\n  modal: React.ReactNode;\n}) {\n  return (\n    <div className=\"flex h-screen\">\n      <aside className=\"w-64 border-r\">{sidebar}</aside>\n      <main className=\"flex-1\">{children}</main>\n      {modal}\n    </div>\n  );\n}\n```\n\nNow `children`\n\n, `sidebar`\n\n, and `modal`\n\ncan each navigate independently.\n\nEach parallel route slot navigates independently. When a user navigates from `/`\n\nto `/settings`\n\n, the `children`\n\nslot updates. The `sidebar`\n\nslot stays exactly where it was — its navigation state is independent.\n\nThis is the key difference from nested layouts. Nested layouts rerender from the changed segment outward. Parallel routes don't affect each other.\n\n```\nUser is at / (children shows home, sidebar shows default nav)\nUser navigates to /settings\n→ children now shows settings\n→ sidebar unchanged, still shows default nav\n\nUser then clicks \"Account\" in sidebar\n→ sidebar now shows account content\n→ children unchanged, still shows settings\n```\n\nWhen a route navigates and a parallel slot doesn't have content for that URL, Next.js needs to know what to show. This is where `default.tsx`\n\ncomes in:\n\n```\napp/\n├── @sidebar/\n│   ├── default.tsx   ← Shown when sidebar has no matching route\n│   └── filters/\n│       └── page.tsx\n└── @modal/\n    └── default.tsx   ← Usually null — modal is hidden by default\n// app/@modal/default.tsx\n// Return null when no modal should be shown\nexport default function ModalDefault() {\n  return null;\n}\n\n// app/@sidebar/default.tsx\n// Sidebar shows its default nav when no specific route is active\nexport default function SidebarDefault() {\n  return <DefaultNavigation />;\n}\n```\n\nWithout `default.tsx`\n\n, navigating to a URL that doesn't match a slot will cause a 404 or error.\n\nThe most compelling use of parallel routes: opening a detail modal without losing the underlying page, then being able to close the modal and return to the page — with full URL sharing support.\n\n```\napp/\n├── @modal/\n│   ├── default.tsx         ← null (no modal)\n│   └── photos/\n│       └── [id]/\n│           └── page.tsx    ← Photo detail modal\n├── layout.tsx\n└── photos/\n    └── page.tsx            ← Photo grid\n```\n\nWhen a user navigates to `/photos/123`\n\n, the `@modal`\n\nslot renders the photo detail while the photo grid stays visible underneath:\n\n``` js\n// app/@modal/photos/[id]/page.tsx\n'use client';\nimport { useRouter } from 'next/navigation';\n\nexport default function PhotoModal({ params }: { params: { id: string } }) {\n  const router = useRouter();\n\n  return (\n    <div \n      className=\"fixed inset-0 bg-black/60 flex items-center justify-center z-50\"\n      onClick={() => router.back()}\n    >\n      <div \n        className=\"bg-white rounded-xl max-w-2xl w-full mx-4 p-6\"\n        onClick={e => e.stopPropagation()}\n      >\n        <PhotoDetail id={params.id} />\n      </div>\n    </div>\n  );\n}\n// app/@modal/default.tsx\nexport default function ModalDefault() {\n  return null; // No modal rendered\n}\n```\n\nThe URL `/photos/123`\n\ncan be shared and when followed directly, the photo modal renders. When navigated to from within the app, the grid stays visible underneath. `router.back()`\n\ncloses the modal and returns to the photo grid.\n\nIntercepting routes extend the modal pattern: intercept a link that would normally navigate away and instead show the content in a modal.\n\n```\napp/\n├── photos/\n│   ├── page.tsx           ← Photo grid\n│   └── [id]/\n│       └── page.tsx       ← Photo page (for direct access)\n└── @modal/\n    ├── default.tsx        ← null\n    └── (.)photos/         ← Intercepts /photos/:id when navigating from same level\n        └── [id]/\n            └── page.tsx   ← Photo in modal\n```\n\nThe `(.)`\n\nsyntax intercepts routes at the same level. `(..)`\n\nintercepts one level up. `(...)`\n\nintercepts from root.\n\nWhen a user clicks a photo link from the grid, `(.)photos/[id]`\n\nintercepts and shows the modal. When they refresh or share the URL `/photos/123`\n\n, Next.js serves `photos/[id]/page.tsx`\n\n— the full page version.\n\n**Use parallel routes for:**\n\n**Don't use parallel routes for:**\n\nThe file system complexity of parallel routes is only worth it when you genuinely need independent URL-based navigation in multiple slots simultaneously.\n\nEach parallel route slot can have its own loading and error states:\n\n```\napp/\n├── @sidebar/\n│   ├── loading.tsx    ← Sidebar loading skeleton\n│   ├── error.tsx      ← Sidebar error state\n│   └── page.tsx\n└── @modal/\n    ├── loading.tsx    ← Modal loading state\n    └── ...\n```\n\nThis means the sidebar can show a skeleton while loading without affecting the main content area, and a sidebar error doesn't break the rest of the page.\n\n```\n// app/@sidebar/loading.tsx\nexport default function SidebarLoading() {\n  return (\n    <div className=\"p-4 space-y-3\">\n      {[...Array(5)].map((_, i) => (\n        <div key={i} className=\"h-8 bg-neutral-100 rounded animate-pulse\" />\n      ))}\n    </div>\n  );\n}\n```\n\nParallel routes use `@named`\n\nfolders to define independent rendering slots in a layout. Each slot navigates independently — changes in one slot don't affect others.\n\nThe essential files: `layout.tsx`\n\naccepts slot props, `default.tsx`\n\nprovides fallback content when a slot has no matching route, `loading.tsx`\n\nand `error.tsx`\n\ngive per-slot loading and error states.\n\nThe modal pattern — pairing parallel routes with intercepting routes for URL-addressable modals that don't replace the underlying page — is the feature's most powerful application.\n\n**Slot showing wrong content after navigation:** Check that `default.tsx`\n\nexists for each slot at every level where navigation could leave the slot without matching content.\n\n**404 when navigating parallel routes:** Usually means a `default.tsx`\n\nis missing. Every `@slot`\n\nfolder that could be in a \"no matching route\" state needs a `default.tsx`\n\n.\n\n**Slots affecting each other:** Parallel route slots are independent — if a change in one is affecting another, check that the layout isn't rerendering more than expected. Use React DevTools to verify slot isolation.\n\n**Refresh shows different content than navigation:** This is the intercepting route working correctly — direct access shows the full page, navigation within the app intercepts to show the modal. If you don't want this behavior, remove the intercept folder.\n\nFor a dashboard with an independent sidebar and an optional modal:\n\n```\napp/\n├── layout.tsx                    # children + sidebar + modal slots\n├── page.tsx                      # Main content default\n├── settings/\n│   └── page.tsx                  # Main content: settings\n├── @sidebar/\n│   ├── default.tsx               # Default sidebar nav\n│   ├── page.tsx                  # Sidebar: home\n│   └── admin/\n│       └── page.tsx              # Sidebar: admin nav\n└── @modal/\n    ├── default.tsx               # null — no modal\n    └── (.)items/\n        └── [id]/\n            └── page.tsx          # Item detail modal\n```\n\nThat structure handles: independent sidebar navigation, optional modals that intercept item links, URL-addressable modal state, and per-slot loading/error states — all with clean URL semantics.", "url": "https://wpnews.pro/news/parallel-routes-in-next-js-app-router-rendering-multiple-pages-in-one-layout", "canonical_source": "https://dev.to/aon_infotech_3a1b6ff525fc/parallel-routes-in-nextjs-app-router-rendering-multiple-pages-in-one-layout-2e0g", "published_at": "2026-07-16 09:35:45+00:00", "updated_at": "2026-07-16 10:04:46.612653+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Next.js", "Pixova"], "alternates": {"html": "https://wpnews.pro/news/parallel-routes-in-next-js-app-router-rendering-multiple-pages-in-one-layout", "markdown": "https://wpnews.pro/news/parallel-routes-in-next-js-app-router-rendering-multiple-pages-in-one-layout.md", "text": "https://wpnews.pro/news/parallel-routes-in-next-js-app-router-rendering-multiple-pages-in-one-layout.txt", "jsonld": "https://wpnews.pro/news/parallel-routes-in-next-js-app-router-rendering-multiple-pages-in-one-layout.jsonld"}}