Creating Modals in Next.js 14: Using Parallel and Intercepting Routes
A Modal is an interactive window that displays on top of the main window, blocking interaction until closed. They are often used to provide important information or gather user input without the need for full-page navigation. This graphical control element prevents users from taking action until they acknowledge the information or complete the task. You can implement React modals using third-party libraries, custom components, or React portals.
However, integrating modals with modern routing has often been challenging. Next.js 14 introduces parallel and intercepting routes, offering you a revolutionary approach to creating modal interfaces. These new routing features address the challenges you encounter when integrating modals with traditional routing patterns.
In this article, you will learn Next.js 14's parallel and intercepting routes and how to create effective modal interfaces using them.
Prerequisites
You will need the knowledge of the following to complete this tutorial:
React fundamentals
Next.js basics
Understanding the Building Blocks
You may notice that the files end in
.tsx
suffix. This is because the project is written in TypeScript. If you are unfamiliar with TypeScript, kindly continue with the.jsx
suffix.
What are parallel routes?
Parallel routes are advance routing features in Next.js. They enable you to render multiple pages simultaneously or conditionally within a shared layout. This results in a more fluid experience, where users can switch between views or sections without full-page reloads or significant navigation interruptions.
Parallel Routes in Next.js are implemented by defining "slots," which are folders named with an '@' prefix before the folder name, and adding a page.tsx
(or .jsx if you do not use typescript). Each parallel route can also have its own loading
, error
, and not-found
states. These slots are then passed as props alongside the children
components to a shared parent layout component.
Take the following steps to integrate parallel routes in your app:
- Install your Next.js app
npx create-next-app@latest
- In your app directory, create folders with the @ prefix (like @folder and @parallel) and add page.tsx
const page = () => {
return <div>Parallelroute</div>;
};
export default page;
- Define your layout component in
app/layout.js
to accept the@parallelroute
slot as props, then render them alongside thechildren
prop. Here's an example of the layout component:
export default function RootLayout({
children,
parallelroute,
}: Readonly<{
children: React.ReactNode;
parallelroute: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
{children}
{parallelroute}
</body>
</html>
);
}
Keep in mind that slots do not impact the URL structure and are not treated as route segments. For example, a route like /modal/@parallelroutes
would remain the same as /modal
, even if @parallelroutes
is a slot.
Best Practices when using Parallel routes
Use meaningful names for parallel route folders: When creating your parallel route folders, it is important to give them descriptive names that reflect their purpose (e.g., @admin, @user, @feed). This makes it easier for you to understand the function of each route at a glance and enhances code readability.
Handle loading and error states for each route: Since parallel routes are independently streamed from the other
children
components, it's important to define customloading
anderror
states for each one. This ensures a smoother user experience, providing users with appropriate feedback (like a loading spinner or an error message) while a route is being fetched or when something goes wrong.Create a default.tsx or [...allroutes] Route: It’s a best practice to have a fallback or default route, such as default.tsx or [...allroutes].tsx, within your parallel route folders. This ensures that when a route doesn’t exactly match any defined paths, the app will render a sensible fallback (like a "page not found" message or a default view).
Parallel routing provides the following benefits:
It lets users switch views easily without reloading the page
It ensures better code organisation by splitting different views into separate files and make the site more responsive by reducing the loading time
Ensures that the layout stays consistent across different sections
Support dynamic content rendering based on user roles or actions
Independent error and loading states for each parallel route
Parallel route is particularly useful for the following:
Modals Split views (like dashboards)
Social media feeds with chat sidebars
Conditional rendering based on certain states
Documentation sites with navigation and content areas
What are Intercepting Routes?
Intercepting routes is a feature in Next.js that overrides or "intercepts" the transmission to the default navigation page. This feature allows you to display different content while preserving the URL. For example, when a user clicks a link that opens a new page, you can intercept the route to display another page while retaining the URL, such as /page/modal
. This creates a more dynamic, app-like experience where users can interact with different parts of the app without being redirected to an entirely new page.
To implement intercepting routes, create a new folder using the intercepting route convention. This new folder, known as the intercepting folder, should begin with a (.)
, (..)
, or (...)
convention, similar to the relative path convention ../
, followed by the name of the folder you wish to intercept.
You can use any of the following syntax:
(.)
intercept segments on the same level(..)
intercept segments at one level higher(..)(..)
intercept segments two levels higher(...)
intercept segments from the root app directory
Take the following steps to integrate parallel routes in your app:
- Create a folder with your intended syntax within the folder from which you plan to start intercepting. For example, to intercept the
about
route in thecontact
route, create(..)about
route within the contact route and add apage.tsx
.
- You can add your code or use this as a demo in the About, Contact and intercepting About Page
About page
const Aboutpage = () => {
return <div>Aboutpage</div>;
};
export default Aboutpage;
Contact Page
import Link from "next/link";
const page = () => {
return (
<div>
<Link href="/about">
<div>Contact Page</div>
</Link>
</div>
);
};
export default page;
Intercepted About Page
const InterceptedAboutpage = () => {
return <div>Intercepted Aboutpage</div>;
};
export default InterceptedAboutpage;
Best Practices
Use Meaningful Names with Syntax Conventions: Ensure that your route names convey the page you plan to intercept and use the correct intercepting route syntax.
Organise routes that share similar contexts or features under common parent routes, especially for authentication or dashboard functionalities.
Avoid Deeply Nested Intercepting Routes: Overly nested routes complicate navigation and make it harder to maintain.
Don't overuse intercepting routes for simple navigation: not every route needs interception.
Use intercepting routes only when essential (e.g., authentication, role-based access control) and rely on standard routing for simple view changes.
Steps to implement a Modal with Parallel route and intercepting routes
Let's assume you intend to display your login page as a modal whenever your users navigate from the app directory.
In the app directory, create a parallel route, as explained earlier.
Add the parallel route slot to the
layout.tsx
page.
export default function RootLayout({
children,
modal,
}: Readonly<{
children: React.ReactNode;
modal: React.ReactNode;
}>) {
return (
<html lang="en">
<body >
{children}
{parallelroute}
</body>
</html>
);
}
- Within the parallel route, create an intercepting route named after the page you intend to intercept, e.g.,
(.)ogin
page.
You can add your code or use this as a demo
"use client";
import { useState } from 'react';
export default function LoginPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e:React.FormEvent) => {
e.preventDefault();
console.log('Data:', { username, password });
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="bg-white p-8 rounded-lg shadow-md w-96">
<h2 className="text-2xl font-bold mb-6 text-center text-gray-800">Login</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="username" className="block text-sm font-medium text-gray-700">Username</label>
<input
type="text"
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">Password</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Log In
</button>
</form>
</div>
</div>
);
}
You'll encounter an error if you run this application and navigate to http://localhost:3000/modal. This error occurs because you haven’t defined any routes for the @modal
slot apart from the (.)login
route. As a result, Next.js can't determine the state for the route other than /login. To fix this, add a default.tsx
file inside the @modal folder. You can return whatever you wish or return null
.
const page = () => {
return null;
};
export default page;
Conclusion
A modal is an overlay window that appears over the main content, preventing user interaction until closed. Modals are commonly used for important information, collect user input, or require user acknowledgment.
For more information on Parallel routes, see Learn Next.js Parallel Routes
For more information on Intercepting routes and Modals, see Learn Next.js Intercepting Routes
For additional information, see Next.js documentaltion on Parallel routes and Intercepting routes
Subscribe to my newsletter
Read articles from Abdul-Majid Aladejana directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by