In this final part of our series, we dive into two incredibly powerful features of Next.js—parallel routes and intercepting routes. These tools allow you to drastically improve the user experience by dynamically loading data and seamlessly switching between different parts of your app. Whether you’re building dashboards, e-commerce sites, or complex interfaces with lots of interactive components, these features can be game-changers in optimizing your application.
Parallel Routes
Parallel routes in Next.js are like having multiple independent UI components working together at the same time. Imagine you have a dashboard where one column shows categories and another shows products. Each component can update independently, improving the user experience by reducing unnecessary reloads and enhancing interface responsiveness.
To implement parallel routes, Next.js uses the naming convention @folder
, and these folders are called slots. These slots are passed as props to the parent layout
, and each slot can render its own part of the page.
Let’s break this down with an example: you have a dashboard with two slots—one for categories, the other for products. When you select a product from the list, we can open its edit page in the same slot.
Here’s what the structure of our dashboard would look like:
Now, let’s see how we can implement this in layout.tsx
. Each slot is automatically passed to the layout as a prop, which you can use for rendering:
export default function DashboardLayout({
children,
categories,
products
}: {
children: React.ReactNode;
categories: React.ReactNode;
products: React.ReactNode;
}) {
return (
<div>
{children}
<div className='grid grid-cols-2 gap-10'>
<div>
{categories}
</div>
<div>
{products}
</div>
</div>
</div>
);
}
One important thing to note: slots are not route segments, so they don’t affect the URL structure in any way.
Benefits of Parallel Routes
Independent Loading
Each slot manages its own loading state or error handling. This is especially useful when different parts of a page load at varying speeds.
Support for Subnavigation
Each slot is a self-contained unit that can have its own navigation. For example, the @products
slot can have a subroute [id]
for displaying product details. When you select a product with id: 1
, the URL changes to /dashboard/1
. You might expect it to be something like /dashboard/products/1
or /dashboard/@products/1
, but remember—slots don’t affect the URL.
When you navigate to /dashboard/1
, Next.js checks which slots correspond to this route. In this case, it matches the products
slot, and the rest are considered missing. What happens next depends on the type of navigation.
Soft and Hard Navigation
With soft navigation (e.g., clicking within the interface), only parts of the UI are updated, providing a smoother user experience. In this case, Next.js retains the state of slots regardless of URL changes. For example, when navigating to /dashboard/1
, the categories slot and the main page remain unchanged.
Hard navigation (e.g., refreshing the page) causes a full reload, resetting the state of all slots. If a slot doesn’t match the URL, Next.js renders the default.tsx
file for that slot. If there’s no default.tsx
, you’ll see a 404
error.
You can use default.tsx
to display static content or even replicate some logic from page.tsx
. For example, in the above case, I replaced parts of the unchanged page.tsx
with default.tsx
(and it works perfectly).
When to Use Parallel Routes?
Parallel routes are ideal for complex UIs where multiple parts of the page need to update without a full reload. Dashboards are the classic use case, but the possibilities don’t end there. You can render article comments alongside the content or display multiple steps of a form on a single screen.
A particularly interesting use case is conditional routes, where slot rendering depends on specific conditions:
import { getUserRole } from "@/utils/auth";
export default function Layout({
admin,
manager,
user
}:{
admin: React.ReactNode,
manager: React.ReactNode,
user: React.ReactNode
}) {
const userRole = getUserRole();
switch (userRole) {
case "admin":
return admin;
case "manager":
return manager;
case "user":
return user;
default:
return <div>No user role</div>
}
}
But this isn’t the end of the road. When combined with intercepting routes, parallel routes unlock even more powerful ways to handle the same URL, offering different rendering experiences.
Intercepting Routes: Adding Context to Navigation
Intercepting routes in Next.js are like a secret trick for enhancing user interactions. Imagine a user browsing through a product catalog and clicking on a product. Instead of redirecting them to a separate page, we can open a modal window without disrupting the context of the current page. This isn’t just a convenience—it’s a significant upgrade to the user experience.
But how does Next.js know when to show a modal versus a full page? This is where intercepting route notations come into play. Let’s break down how they work.
Intercepting Route Notations
Special symbols like (.)
, (..)
, and (...)
allow you to control route behavior as if you’re navigating through a file system. These notations provide flexibility in how your application renders pages:
-
(.)folder
— Intercept the route at the same level -
(..)folder
— Intercept the route one level up -
(..)(..)folder
— Two levels up, and so on -
(...)folder
— Intercept from the root level
Keep in mind: (folder)
groups and @folder
slots are not route segments. This means they don’t count towards the depth of the route’s nesting.
How Does This Work in Practice?
Imagine the user is on the /catalog
page and clicks on a product with the URL /products/chair-4
. If this route is intercepted, Next.js will open the product details in a modal window, while the catalog remains visible in the background. However, if the user navigates to this URL directly, they’ll see a full product page:
In the layout.tsx
for the catalog, we can add an additional prop for the modal window:
export default function CatalogLayout({
children,
modal
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<div>
{children}
{modal}
</div>
);
}
And here’s how the layout.tsx
for the modal might look:
import ProductPage from '@/app/products/[slug]/page';
import ProductModal from '@/components/ProductModal';
import React from 'react';
interface LayoutProps {
children?: React.ReactNode;
params: { slug: string };
}
const ModalLayout: React.FC<LayoutProps> = ({
params
}) => {
return (
<ProductModal>
<ProductPage params={params} />
</ProductModal>
)
};
export default ModalLayout;
Since both the product page and modal content are the same, we simply return the main page from the layout to avoid code duplication.
Why Does This Matter?
Intercepting routes make interfaces more flexible and adaptive. Instead of burdening users with full page reloads, you can dynamically load content while maintaining the current context. This is especially important in applications like e-commerce, where users may want to view multiple products and easily switch between them.
Flexibility and Benefits
By combining intercepting and parallel routes, you can build complex, interactive interfaces where the user retains control of their environment. For instance, you can render multiple blocks simultaneously and manage their states independently. All of this, while preserving context and minimizing reloads.
Tools like parallel and intercepting routes provide developers with the means to create applications that offer smoother navigation and an improved user experience. Leveraging these features allows you to simplify routing while boosting performance and responsiveness. These capabilities extend Next.js’s potential, empowering you to build not just apps but truly modern, dynamic interfaces that seamlessly adapt to user needs.
This wraps up our dive into Next.js and its tools for rendering and routing! We began with the fundamentals of Server Side Rendering, explored the intricacies of server components and actions, delved into the App Router, and now, we’ve concluded with parallel and intercepting routes. These tools not only improve performance and UX but also unlock new possibilities for building scalable, flexible, and responsive applications.
Each topic we covered offers unique opportunities for developers, and together, they provide everything you need to create modern applications with Next.js. May these insights help you build projects that delight users and exceed expectations!
Top comments (0)