
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.
// App.js
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.
// Layout.js
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.
// Home.js
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.
// Gallery.js
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
npm install @reach/dialogyarn
yarn add @reach/dialogAnd here is the code for the ImageView component.
// ImageView.js
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! ๐