DEV Community

Cheryl M
Cheryl M

Posted on • Originally published at cherylm.hashnode.dev on

Create a Modal in React

Implement a Basic Modal

You can view the complete code here https://codesandbox.io/s/naughty-platform-ftyk3s

First, add a div to hold the modal in public/index.html

// public/index.html
<div id="root"></div>
<div id="modal-root"></div>

Enter fullscreen mode Exit fullscreen mode

The modal was put into #modal-root using react portals in src/components/Modal.js.

// src/components/Modal.js
import ReactDOM from 'react-dom';
import styles from '../styles/Modal.module.css';

const Modal = ({ isOpen, onClose }) => {
  if (!isOpen) return null;
  return ReactDOM.createPortal(
    <div className={styles.modal}>
      Modal
      <button className={styles.btn} onClick={onClose}>
        x
      </button>
    </div>,
    document.querySelector('#modal-root')
  );
};

export default Modal;


// src/App.js
import { useState } from 'react';
import Modal from './components/modal';
import styles from './styles/App.module.css';

const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <div>
      <button className={styles.btn} onClick={() => setIsModalOpen(true)}>
        Open Modal
      </button>
      <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />
    </div>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

image.png

The modal is opened with the button (a local state variable in App.js is used to track if the modal is opened or not), and is closed with a button inside the modal.

Positioning the Modal

This will position the modal in a fixed position on top of the content

// Modal.module.css
.container {
  position: fixed;
  top: 50px;
  left: 50%;
  background-color: white;
  border: 2px solid black;
  width: 50%;
  transform: translateX(-50%);
  padding: 10px;
  width: 50vw;
  height: 50%;
  z-index: 1000;
  overflow: auto;
}

Enter fullscreen mode Exit fullscreen mode

Blurring the Background and Disable scrolling

A filter is used to set the background opacity when the modal is opened and the body overflow property is set to 'hidden' to disable scrolling. When the modal is closed, they will be set back to 100% and 'unset' respectively.

// src/App.js
const openModal = () => {
    setIsModalOpen(true);
    document.getElementById('root').style.filter = 'opacity(50%)';
    document.body.style.overflow = 'hidden';
  };

  const closeModal = () => {
    setIsModalOpen(false);
    document.getElementById('root').style.filter = 'opacity(100%)';
    document.body.style.overflow = 'unset';
  };

Enter fullscreen mode Exit fullscreen mode

Now, we use openModal instead of setIsModalOpen(true), and closeModal instead of setIsModalOpen(false)

// src/App.js
<button className={styles.btn} onClick={openModal}>
     Open Modal
</button>
<Modal isOpen={isModalOpen} onClose={closeModal} />

Enter fullscreen mode Exit fullscreen mode

image.png

Closing the Modal by Clicking Outside of the Modal

A ref, and an onClick handler are used to detect when the user clicks outside of the modal (but inside the App container). The modal will only close if it is currently opened. We can make the App container 100vh to cover the height of the browser.

const rootRef = createRef();


const handleClick = (e) => {
    if (rootRef.current.contains(e.target) && isModalOpen) {
      closeModal();
    }
};


<div ref={rootRef} onClick={handleClick}>

Enter fullscreen mode Exit fullscreen mode

Now, App.js becomes

// App.js
import { createRef, useState } from 'react';
import Modal from './components/modal';
import styles from './styles/App.module.css';

const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const rootRef = createRef();

  const openModal = () => {
    setIsModalOpen(true);
    document.getElementById('root').style.filter = 'opacity(50%)';
    document.body.style.overflow = 'hidden';
  };

  const closeModal = () => {
    setIsModalOpen(false);
    document.getElementById('root').style.filter = 'opacity(100%)';
    document.body.style.overflow = 'unset';
  };

  const handleClick = (e) => {
    if (rootRef.current.contains(e.target) && isModalOpen) {
      closeModal();
    }
  };

  return (
    <div className={styles.container} ref={rootRef} onClick={handleClick}>
      <button className={styles.btn} onClick={openModal}>
        Open Modal
      </button>
      <Modal isOpen={isModalOpen} onClose={closeModal} />
      <div>What is Lorem Ipsum?</div>
    </div>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

Top comments (0)