deep-dive2024-04-10Β·11 minΒ·312/348

Next.js 14 Parallel Routes: Complete Usage Guide

How to use parallel routes (@slot) in Next.js 14 App Router for simultaneous page rendering and advanced layouts

Next.js 14 Parallel Routes: Complete Usage Guide

Introduction

Parallel routes are one of the most powerful and least understood features in Next.js 14 App Router. They allow you to render multiple pages simultaneously in the same layout using the @ prefix syntax. This enables patterns like simultaneous dashboard views, split-screen layouts, and isolated error handling per route segment.

I first used parallel routes to build a split-view documentation tool where the table of contents and the content both loaded independently. The user experience improvement was dramatic. This post explains everything I learned.

Environment

  • OS: Windows 11
  • Node.js: v20.10.0
  • Next.js: 14.0.4
  • React: 18.2.0

Problem

Traditional nested routing forces sequential rendering. If a sidebar and main content area are separate route segments, they must both resolve before the page is useful. Slow API calls in one segment block the entire page.

Traditional approach:
/dashboard
  β”œβ”€β”€ layout.tsx (renders  + )
  β”œβ”€β”€ sidebar/page.tsx (slow API call - 2s)
  └── analytics/page.tsx (fast - 200ms)

Result: User waits 2s for the entire page
Parallel routes approach:
/dashboard
  β”œβ”€β”€ layout.tsx (renders @sidebar + @analytics)
  β”œβ”€β”€ @sidebar/page.tsx (loads independently)
  └── @analytics/page.tsx (loads independently)

Result: Both segments load simultaneously

Solution

Basic parallel routes setup:

app/
β”œβ”€β”€ dashboard/
β”‚   β”œβ”€β”€ layout.tsx
β”‚   β”œβ”€β”€ @sidebar/
β”‚   β”‚   β”œβ”€β”€ page.tsx
β”‚   β”‚   └── loading.tsx
β”‚   β”œβ”€β”€ @analytics/
β”‚   β”‚   β”œβ”€β”€ page.tsx
β”‚   β”‚   └── loading.tsx
β”‚   └── page.tsx (default slot)
└── layout.tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
  sidebar,
  analytics,
}: {
  children: React.ReactNode
  sidebar: React.ReactNode
  analytics: React.ReactNode
}) {
  return (
    
{children}
) }

Independent loading states for each slot:

// app/dashboard/@sidebar/loading.tsx
export default function SidebarLoading() {
  return (
    
) }
// app/dashboard/@analytics/loading.tsx
export default function AnalyticsLoading() {
  return (
    
) }

Error boundaries per slot:

// app/dashboard/@sidebar/error.tsx
'use client'

export default function SidebarError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    

Sidebar failed to load

{error.message}

) }

Default slot content:

// app/dashboard/@analytics/page.tsx
// This is the default content when the route doesn't match @analytics

export default function AnalyticsDefault() {
  return (
    

Analytics

Select a metric to view details

) }

Active route matching with parallel routes:

// app/dashboard/layout.tsx
import { notFound } from 'next/navigation'

export default function DashboardLayout({
  children,
  sidebar,
  analytics,
}: {
  children: React.ReactNode
  sidebar: React.ReactNode
  analytics: React.ReactNode
}) {
  // Render 404 if none of the slots can match the route
  if (!sidebar && !analytics) {
    notFound()
  }

  return (
    
{children}
) }

Conditional slot rendering:

// app/(split-view)/layout.tsx
export default function SplitLayout({
  left,
  right,
}: {
  left: React.ReactNode
  right: React.ReactNode
}) {
  return (
    
{left}
{right}
) }

Lessons Learned

  1. Each slot is independent. Slots render simultaneously and independently. A slow slot does not block other slots.

  2. Use loading.tsx per slot. Each slot can have its own loading state, providing granular loading indicators.

  3. Error boundaries are per-slot. If one slot crashes, the others continue to render. This improves error resilience significantly.

  4. Not all routes need all slots. When a URL does not match a slot's route, the slot returns undefined. Handle this gracefully in layouts.

  5. Use parallel routes for any split-view pattern. Dashboard with sidebar, documentation with table of contents, comparison views, and chat applications all benefit from parallel routing.

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.