WebDetailed

Understanding Parallel Routes in Next.js

Parallel routes in Next.js allow multiple pages to match the current URL, displayed within their respective layouts simultaneously. They enable a dynamic and persistent user experience while navigating through different sections of an application.

Implementing Parallel Routes

To use parallel routes:

  1. Rename Folders: Add an @ prefix to folder names representing parallel routes (e.g., @blog, @shop).
  2. Update Layouts: In the layout file, include props matching folder names (e.g., blog and shop).

Here's an example layout:

import React from 'react';

export default function Layout({ children, blog, shop }) {
  return (
    <div>
      <header>My App</header>
      <main>
        {children}
        <section>{blog}</section>
        <section>{shop}</section>
      </main>
    </div>
  );
}

Benefits of Parallel Routes

  1. Built-In Error and Loading Handling: Pages can have error.js and loading.js files for better user feedback during data fetching or error occurrences. Components require manual error boundaries and React.Suspense.
  2. Static Optimization: Next.js optimizes pages for caching and static file generation if conditions are met. Components may not leverage this fully.
  3. State Persistence: Parallel routes maintain their state during soft navigations. This feature is particularly useful for multi-slot layouts.

Managing Persistent Slots

When navigating between pages, slots (e.g., blog, shop) remain active, ensuring layout consistency. However, if a hard refresh occurs and the slots aren’t in the current route, a 404 error appears. To handle this, create a default.tsx file in the respective folders:

export default function DefaultPage() {
  return null;
}

This ensures Next.js renders a fallback for slots not present in the active route.

Use Cases

Login Overlay Example

A login overlay can be implemented as a parallel route, blending seamlessly with the underlying page while maintaining the state of other sections.

// Layout example with slots
export default function Layout({ children, blog, shop }) {
  return (
    <div>
      <nav>My App Navigation</nav>
      <main>
        {children}
        <aside>{blog}</aside>
        <aside>{shop}</aside>
      </main>
    </div>
  );
}

// Login page
export default function Login() {
  return (
    <div className="login-overlay">
      <h2>Login</h2>
      <button onClick={() => router.back()}>Go Back</button>
    </div>
  );
}

The login overlay persists other slots (like blog and shop), and the URL updates to reflect the login route.

Tab Navigation Example

Tabs within a page can also benefit from parallel routes. Each tab represents a page, enabling state persistence across navigations.

// Blog layout with tabs
export default function BlogLayout({ children }) {
  return (
    <div>
      <nav>
        <a href="/blog/top-three">Top Three</a>
        <a href="/blog/archive">Archive</a>
      </nav>
      <section>{children}</section>
    </div>
  );
}

Navigating between tabs like Top Three and Archive does not affect the state of other parallel routes, such as the shop.

Advanced Usage: Loading and Error Handling

By creating loading.js or error.js files within the route folder, Next.js automatically handles asynchronous loading states or errors.

// loading.js
export default function Loading() {
  return <p>Loading...</p>;
}

// Example of an async page
export default async function Shop() {
  await new Promise((resolve) => setTimeout(resolve, 3000));
  return <p>Shop content loaded!</p>;
}

When navigating to the Shop route, the loading.js component renders while the data loads.

Conclusion

Parallel routes in Next.js streamline the development of complex layouts with persistent slots, optimized rendering, and built-in error/loading handling. Use them to create cohesive and dynamic user experiences while leveraging Next.js's powerful features.