DEV Community

Cover image for Create contextual modal navigation with React Router V6.
Mamun Ligzer
Mamun Ligzer

Posted on • Edited on

Create contextual modal navigation with React Router V6.

I'm currently (April 2022) creating a side-project using ReactJs. I have taken inspiration from various existing popular websites like Twitter, Facebook, Trello, etc. I was trying to create an edit-profile UI like Twitter. When you click the Edit Profile button, a pop-up window opens and the URL changes. But the previous page remains in the background. After closing the popup, it goes back to the previous page.

I had no idea how to do it. I searched it on Google, but I found some old tutorials. Note: I am using React Router V6. Finally, I did it. Now, I will show you how I did it.

Let's Start

First thing first, create a react app and install react-router-dom.

npx create-react-app my-app
cd my-app
Enter fullscreen mode Exit fullscreen mode
npm i react-router-dom
Enter fullscreen mode Exit fullscreen mode

I have deleted all of the test files. You can keep them if you want. Create a "Components" folder. Here we will put our homepage and model. Create two files named Modal.js and Main.js inside the "Components" folder. Main.js is our homepage.

Main.js

import { Link, useLocation } from "react-router-dom";

export const Main = () => {
  const location = useLocation();
  return (
    <div>
      <h2>Create contextual modal navigation</h2>
      <Link to="/modal" state={{ background: location }}>
        Open Modal
      </Link>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

Main.js is a react-arrow-functional-component. We have two elements here <h2/> and <Link />. Note: The <Link /> element contains an additional state attribute. It contains an object. We will pass background as key and location as value. We will use this object in the future.

Modal.js

import { useNavigate } from "react-router-dom";

export const Modal = () => {
  const navigate = useNavigate();
  return (
    <div className="modalDiv">
      <div className="modal">
        <h3>Modal</h3>
        <button onClick={() => navigate(-1)}>Close</button>
      </div>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

App.css

.App {
  text-align: center;
}

.modalDiv {
  width: 100vw;
  height: 100vh;
  position: absolute;
  top: 0;
  background-color: rgba(91, 112, 131, 0.4);
  display: flex;
  justify-content: center;
  align-items: center;
}
.modal {
  width: 350px;
  height: 200px;
  background-color: white;
  border-radius: 5px;
}

Enter fullscreen mode Exit fullscreen mode

Index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Router>
    <App />
  </Router>
);

Enter fullscreen mode Exit fullscreen mode

We have wrapped <App /> with <Router /> inside the Index.js file instead of placing it on the App.js file. This is because we will use the useLocation hook used by the React Router in the App.js file. We're not allowed to place any hook used by the React router outside of <Router />.

App.js

import "./App.css";
import { Route, Routes, useLocation } from "react-router-dom";
import { Main } from "./components/Main";
import { Modal } from "./components/Modal";
function App() {
  const location = useLocation();
  const background = location.state && location.state.background;

  return (
    <div className="App">
      <Routes location={background || location}>
        <Route path="/" element={<Main />}>
          <Route path="modal" element={<Modal />} />
        </Route>
      </Routes>
      {background && (
        <Routes>
          <Route path="modal" element={<Modal />} />
        </Routes>
      )}
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

When we click on the open-modal to open the modal, we do not want to show only the modal with a blank page in the background. We want to show the modal on top of the previous page.

So we need to pass the previous location object to <Routes /> instead of using the current location object by default. Thus <Routes /> thinks we are on the same page (previous location). For example, we are on the home page http://localhost:3000/. When we click on the link to open the modal, the position changes to https://localhost:3000/modal but <Routes /> thinks the position has never changed.

Remember? We passed a state attribute in the main.js file, which had a background object. If there is a background object, upon clicking the link to open the modal, the model will be conditionally shown by the second <Routes /> container, and the homepage will be shown as background by the first <Routes /> container.

But when you directly visit the modal page, we will only see the homepage even though we have added the modal route in the first container. You can show the model or any other component for the /model path by simply adding the <Outlet /> element to the Main.js file. For this demo, we will show the model.

Main.js

Add <Outlet/>

import { Link, Outlet, useLocation } from "react-router-dom";

export const Main = () => {
  const location = useLocation();
  return (
    <div>
      <h2>Create contextual modal navigation</h2>
      <Link to="modal" state={{ background: location }}>
        Open Modal
      </Link>
      // Here is the <Outlet/>
      <Outlet />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

I hope, I was able to explain this. If you have any questions or suggestions about this blog, reach out to me via Twitter.

Live Demo.

Source code on GitHub

References

Official React Router modal example

Building a modal module for React with React-Router V5 by Doğacan Bilgili

Top comments (11)

Collapse
 
irenebosque profile image
Irene Bosque

Thanks for the tutorial !👏I was looking exactly how to achieve this

Collapse
 
muhamedoosma profile image
MOHA

is it possible to create this with 'createBrowserRouter' route?

Collapse
 
iamdete profile image
Roselle Tabuena

Hello, I created a blog how to make it work in createBrowserRouter. Hope it helps.

dev.to/roselle_tabuena/how-to-impl...

Collapse
 
adenisov profile image
adenisovgit

same question here. Still can't find info how to open two routes using data router.

Collapse
 
kafkaww profile image
kafka

have you guys found a solution yet?

Thread Thread
 
ligzer_dev profile image
Mamun Ligzer

I've been busy for the past year - I suggest you read the official documentation - I'll write about it when I'm free.

Thread Thread
 
donahldstump profile image
Donald Stump

nothing in the officials docs for whatever reasons

Collapse
 
godwinagedah profile image
Godwin Agedah

How can i achieve this with nested routes

Collapse
 
kpripper profile image
kpripper

Hello! Why can the background component rerender when I close the modal window?

Collapse
 
saadbitar profile image
Saad Bitar

How to make nested Modal Routes. Like for Authentication (Login, Signup, Onboarding, forgot password...)

Collapse
 
itiseternity profile image
Slava Lavrov • Edited

What for do we need background object, if we already render background using Outlet component?