Create a Modal Route in Next.js
πΈ
PexelsIn my previous article, I showed you how to create a modal route in React. Today, Iβll show you how to create a modal route in Next.js using Parallel Routesβ and Intercepting Routesβ.
In case you missed the previous article, you can find it here.
So few days ago i was working on a project where i need to create a modal route in Next.js, and i was thinking how can i create a modal route in Next.js, so i started searching on google and i found a solution to create a modal route in Next.js using Parallel Routes and Intercepting Routes. And trust me you will fall in love with next modal once you will learn to create it.
This allows you to solve common challenges when building modals, such as:
- Making the modal content shareable through a URL.
- Preserving context when the page is refreshed, instead of closing the modal.
- Closing the modal on backwards navigation rather than going to the previous route.
- Reopening the modal on forwards navigation.
Getting Started π
Letβs create a simple example of a product listing page with a modal for product details. Weβll use Parallel Routes for the modal and Intercepting Routes for deep linking.
Folder Structure π
But first understand itβs folder structure because after all next.js is all about folder and files.
app/
βββ layout.tsx
βββ page.tsx
βββ products/
β βββ [id]/
β βββ page.tsx
βββ @modal/
βββ default.tsx
βββ (.)products/
βββ [id]/
βββ page.tsx
Okay now first start from the layout.tsx file.
Step 1 - Change the layout.tsx
file
import Link from 'next/link'
export default function RootLayout({
children,
modal
}: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
<html lang="en">
<body>
<header>
<nav>
<Link href="/">Home</Link>
</nav>
</header>
<main>{children}</main>
{modal}
</body>
</html>
)
}
Watch out for the modal
prop in the RootLayout
component. Otherwise the modal wonβt be rendered. And you will pull your hair out like me.π€
Step 2 - Home page with product listing
import Image from "next/image"
import Link from "next/link"
import { products, type Product } from "@/app/data/products"
export default function Home() {
return (
<div className="min-h-screen bg-gray-100 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
<h1 className="text-4xl font-extrabold text-center text-gray-900 mb-10">
Luxury Products
</h1>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
{products.map((product: Product) => (
<Link
key={product.id}
href={`/products/${product.id}`}
className="group"
>
<div className="bg-white rounded-lg shadow-md overflow-hidden transition-transform duration-300 ease-in-out transform hover:scale-105">
<div className="relative h-64 w-full">
<Image
src={product.image}
alt={product.name}
layout="fill"
objectFit="cover"
className="transition-opacity duration-300 group-hover:opacity-75"
/>
</div>
<div className="p-4">
<h2 className="text-xl font-semibold text-gray-800 mb-2">
{product.name}
</h2>
<p className="text-sm text-gray-600">Click to view details</p>
</div>
</div>
</Link>
))}
</div>
</div>
</div>
)
}
Now we will create the dynamic product page.
Step 3 - Product details page
So we need to create the product details page so that if the user refresh the page then the dynamic product details page should be there. So letβs create the dynamic product details page.
import Image from "next/image"
import Link from "next/link"
import { products, type Product } from "@/app/data/products"
export default function ProductPage({ params }: { params: { id: string } }) {
const product = products.find((p) => p.id === parseInt(params.id))
if (!product) {
return <div>Product not found</div>
}
return (
<div className="min-h-screen bg-gray-100 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto bg-white rounded-lg shadow-md overflow-hidden">
<div className="md:flex">
<div className="md:flex-shrink-0">
<div className="relative h-96 w-full md:w-96">
<Image
src={product.image}
alt={product.name}
layout="fill"
objectFit="cover"
/>
</div>
</div>
<div className="p-8">
<h1 className="text-3xl font-bold text-gray-800 mb-4">
{product.name}
</h1>
<Link
href="/"
className="inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors duration-300"
>
Back to Products
</Link>
</div>
</div>
</div>
</div>
)
}
Now we gonna create the folder called @modal
and inside that folder we will create a file called default.tsx
. default.js file that returns null
. This ensures that the modal is not rendered when itβs not active.
Step 4 - Create the default modal
export default function Default() {
return null
}
So now we will create the modal for the product details page.
Step 5 - Create the product details modal
"use client"
import Image from "next/image"
import Modal from "@/components/modal"
import { products } from "@/app/data/products"
export default function ProductModal({ params }: { params: { id: string } }) {
const product = products.find((p) => p.id === parseInt(params.id))
if (!product) {
return null
}
return (
<Modal>
<div className="flex">
<div className="w-1/2 relative h-96">
<Image
src={product.image}
alt={product.name}
layout="fill"
objectFit="cover"
/>
</div>
<div className="w-1/2 p-6">
<h2 className="text-3xl font-bold text-gray-800 mb-4">
{product.name}
</h2>
</div>
</div>
</Modal>
)
}
And thatβs it! I think now your modal route should work perfectly. If you are still facing any issue then you can check the code on GitHubβ clone it and try to understand. If you want to learn more about it you can check the Nextjs Modalβ. Also you can check the live demoβ.
Thanks I think i should sleep now.π΄
Bye Bye!π
Comments
Leave a comment or reaction below!