How to close a modal in React when clicked outside
Tags: react
Published: 2023-04-06 12:32:13


In this article, we will see how to create a modal in React and close it when clicked outside.

{% embed https://www.youtube.com/watch?v=Hl7o4tJQzPs %}

## Project setup

First, create a react app using the following command:

```bash
npx create-react-app react-modal
```

Now update the `index.css` with the following styles. These are some styles that will be used to style and align the modal:

```css
body {
  display: flex;
  justify-content: center;
}
.modal {
  position: fixed;
  background-color: rgba(0, 0, 0, 0.5);
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.modal-content {
  background-color: white;
  width: 600px;
  border-radius: 5px;
}
.modal-title {
  padding: 0.8rem;
  font-size: 1.5rem;
  border-bottom: 1px solid #aaa;
  text-align: center;
}
.modal-body {
  padding: 1rem;
}
.modal-footer {
  border-top: 1px solid #aaa;
  padding: 0.8rem;
  display: flex;
  justify-content: flex-end;
}
.modal-footer button {
  cursor: pointer;
}
```

In the `index.html` file, which can be found inside the `public` directory, add a div with id `modal-root`:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <div id="modal-root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>
```

Now create a component called `Modal.js` with the following code:

```jsx
import React, { useEffect } from "react"
import ReactDOM from "react-dom"

const Modal = ({ onClose }) => {
  useEffect(() => {
    document.body.style.overflow = "hidden"
    return () => {
      document.body.style.overflow = "auto"
    }
  }, [])
  return ReactDOM.createPortal(
    <div className="modal">
      <div className="modal-content">
        <div className="modal-title">Modal Title</div>
        <div className="modal-body">
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quaerat,
          sint ab ex odio pariatur et eius? Nam, quod eum adipisci earum nisi
          tempora, nesciunt esse voluptate illo, maxime consectetur harum!
        </div>
        <div className="modal-footer">
          <button onClick={onClose}>Close</button>
        </div>
      </div>
    </div>,
    document.getElementById("modal-root")
  )
}

export default Modal
```

Here we are creating a [react portal](https://reactjs.org/docs/portals.html) and mounting it to the div with the id `modal-root`, created earlier.
We need to create the modal in a separate div outside the root div so that we can render the modal on top of the existing content.

Here we have a useEffect hook, which is used to hide the scrollbar when the modal is open so that the scrolling is blocked when the modal is open.

Now in `App.js`, create enough content to induce a scrollbar and add a button, when clicked will open the modal.

```jsx
import { useState } from "react"
import Modal from "./Modal"

function App() {
  const [isModalOpen, setIsModalOpen] = useState(false)
  return (
    <div>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Distinctio
        vero facere necessitatibus ipsum, veritatis autem natus eius, fugiat hic
        eligendi dignissimos voluptatum. Recusandae corrupti dolorem consectetur
        sequi excepturi inventore facilis! Consequatur non ullam magni fugiat
        natus expedita saepe voluptatem impedit est odio! Sint consequatur aut
        maiores illum. Libero eos sapiente adipisci aliquid nisi magnam ad qui
        minus voluptate, id et? Unde eum ea excepturi expedita ipsa debitis
        corrupti? Ex voluptatem assumenda velit consectetur! Rerum, rem
        laudantium accusantium vitae vero ipsa delectus corporis quibusdam
        nihil, explicabo aliquam necessitatibus dignissimos sint labore. Optio
        itaque quo earum iusto, ipsum eius culpa consequatur, nihil, tempore
        inventore a dolor eos deserunt vitae sit aliquam? Molestiae quia
        inventore omnis pariatur consectetur sit sed, labore mollitia quis!
        Voluptatem consequatur sit corporis itaque, repudiandae temporibus id
        sint similique qui? Odio minus nam quia dignissimos fuga consectetur
        commodi minima adipisci nulla eius optio sunt explicabo quidem, vero
        tenetur voluptatibus? Ex possimus iste necessitatibus vero commodi natus
        iure tempore dicta neque laudantium, libero ullam odit quibusdam nobis
        voluptate molestiae. Mollitia facere iusto maiores eius quisquam ab
        architecto voluptatum veniam exercitationem. Suscipit magnam veritatis
        enim? Qui excepturi nemo aperiam numquam libero assumenda recusandae
        quos dignissimos possimus mollitia non deleniti aut expedita soluta
        nobis eaque, tempora laboriosam animi consectetur nam sit veniam?
        Voluptatem temporibus, saepe quos eum enim ipsum accusamus qui? Odio
        quidem magni reprehenderit dolores saepe nemo tenetur eaque labore
        assumenda. Explicabo placeat non mollitia a quos, dolor laborum suscipit
        quaerat! Recusandae, nemo adipisci nihil ipsum vero quia accusantium
        non? Eligendi in ex quo sint repellat ab, nemo delectus. Consequuntur
        non officia eos temporibus quis odit dolor ratione praesentium quae ex.
        Iste vel necessitatibus, facilis, minima nihil ullam obcaecati non ipsam
        tenetur dicta fugit explicabo accusantium rerum autem? Dolores vero quia
        nam animi quis, non deserunt officiis quasi natus ipsa accusamus.
        Dolores optio saepe dignissimos fugiat autem aliquid perspiciatis,
        consequuntur provident? Dignissimos eligendi facilis vero eaque,
        consequatur tempore, labore aperiam, cupiditate dolores praesentium
        amet? Eius nemo quisquam animi enim blanditiis doloremque? Inventore
        quisquam officiis itaque animi culpa suscipit, iusto obcaecati sapiente,
        facere architecto tenetur, laudantium repellendus quis nisi! Voluptatem
        adipisci fugit facere libero consequuntur magni consequatur, totam
        magnam necessitatibus minima illum? Possimus a commodi labore architecto
        autem dicta officiis harum vitae quos repellendus, nesciunt
        exercitationem amet, expedita libero explicabo, dolores sapiente
        laudantium modi? Excepturi explicabo debitis neque optio corporis non
        atque. Dolorem aliquid sed, quasi officia dolores temporibus rem nulla
        eligendi fugit consectetur praesentium, iusto quam! Ipsam voluptatum
        assumenda facere, saepe, iste maiores placeat, earum quos neque at
        deleniti omnis eos. Ipsam sequi provident quis qui mollitia doloribus
        magnam recusandae et ratione earum dolore fugit itaque, laborum eligendi
        similique inventore. Incidunt quo officia et ea minus soluta voluptas
        est illum minima! Libero, voluptate. Illum praesentium in suscipit
        numquam necessitatibus beatae quaerat officia architecto.
      </p>
      <button onClick={() => setIsModalOpen(true)}>Open Modal</button>
      {isModalOpen && <Modal onClose={() => setIsModalOpen(false)} />}
      <p>
        Lorem ipsum dolor, sit amet consectetur adipisicing elit. Illum iure id
        repudiandae, aspernatur ea ut corporis provident explicabo minus odit
        commodi, labore optio unde delectus tenetur vitae necessitatibus. Ullam,
        illo. Maxime doloremque rerum eius quae aspernatur accusantium tempore
        nobis, sapiente officia, non necessitatibus aperiam omnis assumenda
        quisquam cum esse eligendi nemo laudantium nihil labore aliquid modi
        vel! Quos, quo tempora! Dolore libero quos alias ipsum unde dolor nisi
        sed perferendis laborum non sunt corporis a quam aliquam, eveniet
        doloribus laboriosam sequi cupiditate deleniti, molestiae recusandae, ut
        labore minima! Vero, eos. Reiciendis nostrum itaque placeat perferendis
        doloremque earum cupiditate nihil voluptatibus officia excepturi odio
        ipsam molestias similique quibusdam, doloribus minus culpa. Provident
        similique dolor assumenda magnam eveniet, vitae aspernatur recusandae
        nesciunt. Culpa sint minus sunt iste distinctio ipsa perferendis debitis
        nostrum provident? Neque totam doloribus obcaecati, accusantium aliquid
        suscipit nobis alias illo nostrum eius harum quasi explicabo voluptatem
        architecto molestiae veritatis. Quis quibusdam eius perferendis odio
        illum et unde! Repellat facilis autem earum quidem voluptatem. Itaque
        cumque ipsum nostrum libero corrupti sequi temporibus voluptas commodi
        quia, molestiae a unde eveniet aperiam. Commodi veniam aspernatur nam
        quas blanditiis tempore harum hic? Perspiciatis, excepturi! Ratione ex
        vero reiciendis cum eos est vitae eius dignissimos quo necessitatibus
        aut fugit amet dicta, deserunt ad libero. Animi incidunt dolorem magnam
        natus qui rem inventore impedit, accusamus ratione, consequatur
        repellendus harum error autem nemo quos repudiandae itaque, amet beatae.
        Aut, fugit ea necessitatibus odio veniam illo sit? Explicabo quasi fuga
        non sed magni corporis tenetur delectus dolore! Praesentium rerum facere
        illo delectus harum commodi porro repudiandae quas! Sint eligendi
        dolore, minima nostrum facilis dolorum quisquam dolores amet? Vitae
        incidunt, corrupti dolorum, voluptatem mollitia aspernatur perspiciatis
        dicta repudiandae ut eligendi id et vero. Consectetur illum ipsum unde!
        Iste porro ab nobis officiis, vero debitis consectetur ipsa error
        reprehenderit? Nulla eaque unde saepe inventore quas? Mollitia
        aspernatur eum expedita! Ipsa quia maxime repellendus non nisi.
        Accusamus, doloremque. Obcaecati quisquam voluptates natus reprehenderit
        ratione cupiditate iusto tenetur soluta doloremque. Facilis. Quo id
        illum voluptatibus quod ducimus error similique ut facere, quas iusto
        eius eveniet consectetur deserunt magnam natus ex. Culpa beatae
        voluptatem sit inventore odit voluptate reprehenderit fuga, qui
        mollitia! Doloribus rerum ipsam atque, laudantium impedit consequatur
        doloremque corrupti optio laboriosam incidunt unde sint, aliquam velit
        dolor quod autem, quisquam maxime est provident porro culpa? Corporis
        voluptas aut suscipit nesciunt. Sed itaque nihil, sit eaque amet odit
        libero necessitatibus illo blanditiis ut tempora explicabo. Eius odio
        ducimus hic voluptates, voluptatibus culpa velit consequuntur tempora
        quaerat aliquid quas. Explicabo, soluta voluptatibus. Quod dolorem eaque
        consequatur neque ipsa laborum fuga minus? Atque culpa cupiditate,
        aperiam quod soluta voluptatum sint sit iure minus asperiores rerum.
        Minima officia animi, molestiae dolorem autem reprehenderit a? Porro
        excepturi, corporis nihil et rerum recusandae minus impedit quaerat
        sapiente ducimus earum ipsa aspernatur vero magnam maiores quae facilis
        laudantium? Illum ipsam aperiam sunt voluptatum. Delectus explicabo
        laborum maxime! Reprehenderit maxime dignissimos cumque tenetur
        necessitatibus maiores molestiae quisquam ipsum explicabo. Consequuntur
        ad sapiente reprehenderit, ipsum et exercitationem quod dolore!
        Consectetur nihil earum aliquam aspernatur, excepturi consequuntur
        tempore libero autem. Vitae enim nihil ut? Veniam consequatur explicabo
        fuga commodi assumenda? Laborum cum non voluptatibus error accusantium
        fugit, deleniti iusto et. Quidem culpa minima fuga unde, deserunt facere
        modi dolorem voluptatibus? Delectus, fugit ducimus magnam non ut quos
        aspernatur iste impedit qui, dignissimos excepturi eos corrupti
        voluptatum, repellendus ipsam deleniti suscipit harum repellat
        voluptates dolorem. Alias id fuga laboriosam eligendi quibusdam? Libero
        ullam pariatur incidunt optio a voluptas, iusto laboriosam cumque quas
        architecto numquam tempora inventore maiores dignissimos explicabo
        suscipit distinctio vero provident. Fugit nobis reiciendis
        exercitationem soluta dicta dolorum consequuntur. Consequatur itaque
        maxime quaerat. Aut rem praesentium deleniti dolor odio nisi, dolores at
        eveniet cupiditate expedita maiores optio veritatis esse impedit beatae
        commodi nostrum quam delectus explicabo laboriosam asperiores molestiae!
        Omnis culpa iste illo perferendis vitae reprehenderit facere, voluptate
        aspernatur accusantium ad sit, animi velit ex dolor eos, laborum
        molestias? A porro maiores, nostrum laborum non mollitia unde aspernatur
        quo. Voluptatibus maxime quis blanditiis vitae cupiditate quo vel
        voluptates consectetur sequi commodi dolorem, rerum, maiores officiis
        nobis expedita! Ducimus nostrum vitae omnis ad rerum atque animi ipsum!
        Quisquam, doloribus autem! Aut sapiente explicabo maiores dolores
        commodi tempore eligendi quibusdam distinctio ducimus, consequuntur in
        enim nobis rerum mollitia aspernatur odio deserunt officia veniam beatae
        officiis. Quibusdam tempore numquam dolore officia ad? Consequatur
        repellat hic atque commodi amet error unde voluptates mollitia illum
        placeat eaque maiores dolorem corrupti, quae provident magnam incidunt
        earum. Libero praesentium ipsa voluptates, in architecto corporis
        aliquid nostrum. Veniam aperiam perspiciatis aliquam placeat, excepturi
        recusandae vel ex! Mollitia fugit blanditiis vel! Quaerat voluptas illum
        non laboriosam quia voluptatem amet tenetur debitis nobis totam! Sint
        perferendis velit maiores fugiat. Voluptatum at praesentium doloremque
        amet illum magni provident tenetur earum qui harum ex voluptatibus
        aliquid, nemo culpa. Totam corrupti, quas, quos nesciunt facere eius
        delectus optio recusandae voluptatibus debitis quasi. Quasi,
        voluptatibus consectetur ipsum accusantium molestiae est voluptatum
        aperiam? Delectus architecto, culpa et labore repudiandae minus
        quibusdam autem inventore adipisci eligendi laborum obcaecati quasi
        maxime! Molestiae minima laborum maiores laudantium. Facere inventore
        eligendi quibusdam corporis nulla ab dolore, ipsam et ea consectetur est
        aliquam rem deserunt culpa deleniti cumque unde exercitationem
        laudantium labore? Nulla, tenetur ducimus delectus quia totam amet. Quia
        neque sed ad incidunt, voluptas veniam voluptatum mollitia soluta
        doloribus temporibus qui debitis eveniet sequi aut ullam impedit
        voluptates molestias ipsa alias vitae illum. Ex nostrum obcaecati sequi
        odio. Consequuntur laborum odit ut voluptatibus adipisci officiis,
        aperiam recusandae enim atque commodi dignissimos error magni sunt
        libero illo! Iusto rerum pariatur nisi quaerat, repellendus soluta vitae
        velit labore expedita officiis! Ratione totam nemo accusamus. Nemo autem
        nesciunt, sequi quia eaque, est, odio esse praesentium debitis aperiam
        voluptatem omnis deleniti. Omnis sequi sunt quasi modi dolore rem quam
        accusamus similique voluptas. Reprehenderit eaque esse sint inventore
        facere numquam soluta nemo unde, necessitatibus rerum vel quos deleniti
        similique, atque perspiciatis recusandae. Numquam, illum nemo unde
        necessitatibus fuga perspiciatis reprehenderit a sunt iste! Perferendis
        pariatur numquam neque iure tenetur nisi maxime exercitationem inventore
        tempore harum quaerat officiis magni fugit laboriosam placeat in, est
        hic repudiandae molestias odio. Voluptatibus laboriosam alias temporibus
        eveniet laudantium! Nesciunt doloremque exercitationem quae quas
        consequatur ex suscipit odio, doloribus delectus? Consectetur culpa
        earum non, inventore, iste architecto libero facere eligendi a,
        explicabo sed nostrum? Rem fuga voluptas ea voluptatem. Sint, voluptate
        voluptatibus! Similique voluptate et quibusdam veniam perspiciatis
        deserunt impedit earum, optio consequatur voluptatum est reprehenderit
        in beatae sapiente hic pariatur rem distinctio. Iure expedita ipsum
        impedit minus harum. Dolore illum repellendus cupiditate quo dolor sint,
        deserunt nesciunt magni corporis inventore eos quaerat vitae quas esse
        minus dolorum illo suscipit soluta repudiandae, blanditiis ipsam
        commodi, consequatur eaque doloremque.
      </p>
    </div>
  )
}

export default App
```

In the above code, we are maintaining a local state to store if the modal is open.
If the state is true, we are displaying the modal. We are passing a callback named `onClose`,
which will set the state to false so that the modal can be closed.

Now, if you run the app and click on the button, you should be able to see the modal.

## Closing the modal when clicked outside

As of now, if you click outside the modal, nothing will happen.
To close the modal when the user clicks outside,
first, we need to [detect if the user has clicked outside](https://www.codingdeft.com/posts/react-on-click-outside/).

For that we can make use of refs:

```jsx
import React, { useEffect, useRef } from "react"
import ReactDOM from "react-dom"

const Modal = ({ onClose }) => {
  const ref = useRef()
  useEffect(() => {
    const checkIfClickedOutside = e => {
      if (ref.current && !ref.current.contains(e.target)) {
        onClose()
      }
    }
    document.addEventListener("click", checkIfClickedOutside)
    return () => {
      document.removeEventListener("click", checkIfClickedOutside)
    }
  }, [onClose])

  useEffect(() => {
    document.body.style.overflow = "hidden"
    return () => {
      document.body.style.overflow = "auto"
    }
  }, [])
  return ReactDOM.createPortal(
    <div className="modal">
      <div className="modal-content" ref={ref}>
        <div className="modal-title">Modal Title</div>
        <div className="modal-body">
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quaerat,
          sint ab ex odio pariatur et eius? Nam, quod eum adipisci earum nisi
          tempora, nesciunt esse voluptate illo, maxime consectetur harum!
        </div>
        <div className="modal-footer">
          <button onClick={onClose}>Close</button>
        </div>
      </div>
    </div>,
    document.getElementById("modal-root")
  )
}

export default Modal
```

Here we have created a reference to the `modal-content` div and in the useEffect we check if the div contains (is inside) the clicked target.
If that is not the case (not condition), then we close the modal.

## Source code and Demo

You can view the complete [source code here](https://github.com/collegewap/yt-react-modal) and a [demo here](https://yt-react-modal.vercel.app/).