NextjsParallel Routes

Parallel Routes

A parallel route lets a single layout render more than one page at the same time, each independently. You define one by naming a folder with an @ prefix — a slot— and each slot behaves like its own mini page tree, with its own loading and error states, that the parent layout can place anywhere it wants.

Folder structure

Text
app/
  dashboard/
    @analytics/
      page.tsx
    @team/
      page.tsx
    layout.tsx
    page.tsx
The layout receives each slot as a prop matching the slot's folder name (without the @), alongside the regular children prop for the unnamed default slot.

app/dashboard/layout.tsx

TSX
export default function DashboardLayout({
  children,
  analytics,
  team,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <div className="dashboard-grid">
      <section className="main">{children}</section>
      <section className="analytics">{analytics}</section>
      <section className="team">{team}</section>
    </div>
  )
}
Classic use case: an independent-panel dashboard
Parallel routes are a natural fit for dashboards where separate panels load, error, and stream in independently — an analytics chart that's slow to compute shouldn't block a team-activity feed from rendering. Because each slot is its own subtree, it can define its own loading.tsx to stream in as soon as it's ready, without waiting for its sibling slots.
Another classic use case is a modal that needs to render alongside the main content rather than replacing it — for example, a photo viewer that pops up over a feed while the feed stays mounted underneath. That pattern is usually built by combining a parallel route slot (for the modal) with an intercepting route, so keep reading there for the full picture.
default.tsx: filling the gap when a slot doesn't match

Slots don't all need a matching sub-route for every URL under the layout. When the current URL doesn't match anything inside a given slot, Next.js needs something to render for that slot instead — and by default, that's a 404 for the whole layout, which is rarely what you want.

app/dashboard/@team/default.tsx

TSX
export default function Default() {
  // Rendered whenever the current URL doesn't match a page
  // inside the @team slot — keeps the rest of the layout intact.
  return null
}
Note
Add a default.tsx to every slot that won't have a matching route for every possible URL under that layout. It's the fallback that prevents an unrelated navigation elsewhere in the layout from producing an unexpected 404 for a slot that was never meant to handle that URL.
Tip
Slots are matched independently on client-side navigation, so navigating within one slot doesn't reset the state of the others — reinforcing why parallel routes suit dashboards where each panel should behave like its own self-contained app.
  • A folder prefixed with @ defines a named slot, passed to the parent layout as a prop with that same name.

  • Each slot is its own independent page tree — with its own loading.tsx and error.tsx if needed.

  • The unnamed children prop is the default slot every layout already has.

  • Add default.tsx to a slot to avoid a 404 when the current URL has no matching route inside that slot.