Building a Modal Route with React Router
I don’t know about you, but I love modals. Specifically, when they’re used to display content without navigating away from the current page.
And today, I’ll show you how to create a modal route in React with the help of the react-router-dom library.
if some of you are not understanding what we are going to build, then you can see the stackblitz live demo example.
Use Case 🧐
- Suppose you have a list of items and you want to show the details of the item in a modal and without navigating to a new page.
- You want to show a form in a modal and submit the form without navigating to a new page.
Imagine how cool it would be to navigating to a new link without leaving the current page.
Okay enough talking, let’s start.
Getting Started 📌
I’m assuming you have a basic understanding of React and React Router . If not, I recommend checking out the official documentation. and also you have a basic react project setup.
Step 1 - Setup React Router
First, let’s set up the React Router in our project. We’ll be use the createBrowserRouter
for define our routes.
import * as React from 'react'
import { Dialog } from '@reach/dialog'
import {
createBrowserRouter,
Link,
Outlet,
RouterProvider,
useNavigate,
useParams,
} from 'react-router-dom'
import '@reach/dialog/styles.css'
import { getImageById, IMAGES } from './images'
const router = createBrowserRouter([
{
path: '/',
Component: Layout,
children: [
{
path: '/',
Component: Home,
},
{
path: 'gallery',
Component: Gallery,
children: [
{
path: 'img/:id',
Component: ImageView,
},
],
},
],
},
])
export default function App() {
return <RouterProvider router={router} />
}
Step 2 - Create the Layout
The Layout component acts as the main structure of our application. It includes a navigation menu and an Outlet to render child routes.
export function Layout() {
return (
<div>
<h1>Outlet Modal Example</h1>
<p>
This is a modal example using `createBrowserRouter` that drives modal
displays through URL segments. The modal is a child route of its parent
and renders in the `Outlet`.
</p>
<div>
<nav>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/gallery'>Gallery</Link>
</li>
</ul>
</nav>
<hr />
</div>
<Outlet />
</div>
)
}
Now we have to create the Home Page.
Step 3 - Defining the Home Page
The Home
page is the default route of our application. It contains a link to the Gallery route. It’s will be just a basic page with a link to the Gallery route.
export function Home() {
return (
<div>
<h2>Home</h2>
<p>
Click over to the <Link to='/gallery'>Gallery</Link> route to see the
modal in action.
</p>
<Outlet />
</div>
)
}
Step 4 - Create the Gallery Component
The Gallery component displays a grid of images. Each image is a link that opens a modal by changing the URL to a child route.
export function Gallery() {
return (
<div style={{ padding: '0 24px' }}>
<h2>Gallery</h2>
<p>
Click on an image to open a modal. You'll notice that the URL changes,
and you still see this route behind the modal. The modal is a child
route of <pre style={{ display: 'inline' }}>"/gallery"</pre>.
</p>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
gap: '24px',
}}
>
{IMAGES.map((image) => (
<Link key={image.id} to={`img/${image.id}`}>
<img
width={200}
height={200}
style={{
width: '100%',
aspectRatio: '1 / 1',
height: 'auto',
borderRadius: '8px',
}}
src={image.src}
alt={image.title}
/>
</Link>
))}
<Outlet />
</div>
</div>
)
}
And finally, we have to create the modal component to display the image.
Step 5 - The Modal Component
The ImageView
component represents the modal itself. And for modal we gonna use the
@reach/dialog
package.
npm
bash filename="terminal" copy npm install @reach/dialog
And here is the code for the ImageView
component.
export function ImageView() {
let navigate = useNavigate();
let { id } = useParams<"id">();
let image = getImageById(Number(id));
let buttonRef = React.useRef<HTMLButtonElement>(null);
function onDismiss() {
navigate(-1);
}
if (!image) {
throw new Error(`No image found with id: ${id}`);
}
return (
<Dialog
aria-labelledby="label"
onDismiss={onDismiss}
initialFocusRef={buttonRef}
>
<div
style={{
display: "grid",
justifyContent: "center",
padding: "8px 8px",
}}
>
<h1 id="label" style={{ margin: 0 }}>
{image.title}
</h1>
<img
style={{
margin: "16px 0",
borderRadius: "8px",
width: "100%",
height: "auto",
}}
width={400}
height={400}
src={image.src}
alt=""
/>
<button
style={{ display: "block" }}
ref={buttonRef}
onClick={onDismiss}
>
Close
</button>
</div>
</Dialog>
);
}
Woohoo! 🥳 You have successfully implemented the modal using @reach/dialog
and react-router-dom
.
Trust me, it’s a great feeling to create something like this. 🚀
Complete Code
If you need the complete code, you can find it here and find the live demo stackblitz here.
Thanks for reading! 🙌
Comments
Leave a comment or reaction below!