# Creating an Efficient React Modal Component with Hooks and Portals

> Learn how to build an efficient modal component in React using Hooks and Portals. This guide will walk you through creating a modal with dynamic content, slick animations, and a responsive UI, enhancing the overall user experience.

- **Source:** DevDreaming (https://devdreaming.com)
- **Canonical URL:** https://devdreaming.com/blogs/create-efficient-modal-react-portals
- **Author:** CodeBucks
- **Published:** 2021-06-08
- **Last updated:** 2023-10-30
- **Topics:** User Experience, React js

---

The modal dialog or simply a "modal" is an important element in user experience design. It serves as an interactive dialog box or popup window superimposed on the current webpage. While modals can be effective when designed correctly, they can be a source of annoyance😫 for users if not optimized. A high-quality modal should offer a seamless [UX](/blogs/category/user-experience)

In this walkthrough, you'll craft a top-notch **React modal component**🎨 without relying on external libraries.

Demo Link🖤: [React Modal using Portals](https://react-reusable-components.vercel.app/)

### Here's a rundown of what we'll cover:

Our main goal is to create an efficient modal which,

- Crafting a visually pleasing modal layout
- Ensuring the modal doesn't disrupt the UI, especially with parent component overflow
- Introducing dynamic content rendering capabilities
- Incorporating sleek animations
- Implementing intuitive user controls, like outside-click to close

If you're more of a visual learner, check out the complementary video tutorial📽👇:

[▶ Watch the video on YouTube](https://www.youtube.com/watch?v=5aG8peJsBVg)

Let's get started!

### Setting Up The Project

Create your React App using,

```shell
npx create-react-app react-modal
```

For this tutorial i'm going to use `Sass` so make sure you have installed `sass` package. To install it do,

```shell
npm install sass
```

> **Update**
>
> In the video I have used `node-sass` which is deprecated so we will use `sass`.

### Modal basic structure

Clean up🧹 the `App.js` of any extra code and introduce a button to trigger the modal, as demonstrated below:

```javascript
<div className="App">
  <button>Modal</button>
</div>
```

To manage the modal's open and close state, let's use the `useState` hook 👇:

```javascript
import { useState } from "react";
import "./App.scss";

function App() {

  const [modal, setModal] = useState(false);
  const Toggle = () => setModal(!modal);

  return (
    <div className="App">
      <button className="clickme" onClick={Toggle}>
        Modal
      </button>
    </div>
  );
}

export default App;
```

**Line 6:** This line contains `modal` state which is false initially.

**Line 7:** A `Toggle` method to toggle `modal` state from false to
true and vice-versa.

**Line 11:** Make sure to connect `Toggle()` method to `onClick` of the button.

Proceed to create the `Modal.js` file, and start shaping the modal component:

```javascript
const Modal = () => {
  return (
    <div>
      Modal
    </div>
  );
};

export default Modal;
```

Integrate the `Modal` component into `App.js` and pass the modal state as props:

```javascript
import { useState } from "react";
import "./App.scss";
import Modal from "./Components/Modal";

function App() {

  const [modal, setModal] = useState(false);
  const Toggle = () => setModal(!modal);

  return (
    <div className="App">
      <button className="clickme" onClick={Toggle}>
        Modal
      </button>
      <Modal show={modal} />
    </div>
  );
}

export default App;
```

**Line 17:** Here we have imported `Modal` component. And passed `modal` state as `show` in the props.

Update `Modal.js` with the following structure and functionalities👇:

```javascript
import Close from "./times-solid.svg";

const Modal = ({ show }) => {
  return (
    <>
     <div
        className={`modalContainer ${show ? "show" : ""} `}
      >
        <div className="modal" >
          <header className="modal_header">
            <h2 className="modal_header-title"> Modal Title </h2>
            <button className="close" >
              <img src={Close} alt="close" />
            </button>
          </header>
          <main className="modal_content">
          This is Modal Content
          </main>
          <footer className="modal_footer">
            <button className="modal-close" >
              Cancel
            </button>

            <button className="submit">Submit</button>
          </footer>
        </div>
      </div>
    </>
  );
};

export default Modal;
```

**Line 3:** Deconstruct `show` from the props.

**Line 7:** We will display modal only when `show` state is true so when the show state is true we will add the `show` class.

**Line 9 to 30:** This is the whole `Modal layout`.

- `ModalContainer` div contains the modal
- In the modal div, There is one header which contains `modal title` and `close` Button (You can use any icon for close button).
- `Main` tag contains `content` of the modal.
- Footer has 2 buttons one is `submit` and another is `cancel`.

#### Styling The Modal

Now when you press a button, the modal will show and on pressing again it will close the modal. First Let's add some styling to our modal. Create `Modal.scss` file and import it in the `Modal.js` file. Copy and paste the following styles in the `Modal.scss` file.

```scss
.modalContainer {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba($color: #000000, $alpha: 0.35);

  display: flex;
  justify-content: center;
  align-items: center;

  opacity: 0;
  visibility: hidden;
  transition: all 0.2s ease;

  .modal {
    width: 30vw;
    height: auto;
    background-color: #fff;
    padding: 2rem;
    border-radius: 20px;

    background-color: rgba(255, 255, 255, 0.35);
    backdrop-filter: blur(5px);
    box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.2);

    &_header {
      position: relative;
      border-bottom: 1px solid #dddddd;
      &-title {
        text-align: center;
      }

      .close {
        position: absolute;
        top: 0;
        right: 0;
        background: transparent;

        img {
          width: 1rem;
          height: auto;
          transition: all 0.3s;
        }
        &:hover {
          img {
            transform: scale(1.1);
          }
        }
      }
    }

    &_content {
      border-bottom: 1px solid #dddddd;
      padding: 2rem 0;
    }

    &_footer {
      padding: 2rem 0;
      padding-bottom: 0;

      button {
        float: right;
        padding: 0.5rem;

        border-radius: 8px;
      }
      .modal-close {
        background-color: transparent;
        font-weight: 600;

        &:hover {
          color: rgba(54, 67, 72, 0.8);
        }
      }
      .submit {
        margin-right: 1rem;
        background-color: #364348;
        color: #fff;

        &:hover {
          background-color: rgba(54, 67, 72, 0.8);
        }
      }
    }
  }
}

.modalContainer.show {
  visibility: visible;
  opacity: 1;

  .modal {
    transform: translateY(0);
  }
}
```

This will give your modal a better look. Also, by applying the `backdrop-filter` you can make it look like frost-glass. First we have kept opacity to 0 and visibility to hidden then in the last part we have added one class called show so when the class is enabled it will change the visibility and opacity values.

#### Adding a Close Event in Modal

In the `App.js` file pass `toggle` method as a props in the modal just like the following line 👇

```javascript
<Modal show={modal} title="My Modal" close={Toggle} />
```

open `Modal.js` file and deconstruct `close` from the props.

```javascript
import Close from "./times-solid.svg";

const Modal = ({ show, close }) => {
  return (
    <>
     {
     show ?

      <div
        className="modalContainer"
        onClick={() => close()}
      >
        <div className="modal" onClick={(e) => e.stopPropagation()}>
          <header className="modal_header">
            <h2 className="modal_header-title">Modal Title</h2>
            <button className="close" onClick={() => close()}>
              <img src={Close} alt="close" />
            </button>
          </header>
          <main className="modal_content">This is modal content</main>
          <footer className="modal_footer">
            <button className="modal-close" onClick={() => close()}>
              Cancel
            </button>

            <button className="submit">Submit</button>
          </footer>
        </div>
      </div>
      : null
     }
    </>
  );
};

export default Modal;
```

**Line 3:** Deconstruct `close` from the props.

We have added close method in 3 places:

**Line 16**: At the `close` button.

**Line 22**: At the `cancel` button.

**Line 11**: We have also added `close` method here too. Why? because whenever user clicks outside it should close the modal. So here, when user clicks on the `modalContainer` it closes the modal.

**Line 13**: here we have to stop this `click` events in the modal else it will close it so for that we have used `e.stopPropagation()`.

> **hint:**
>
>  You can also add event Listener and add functionality in which when user clicks esc key, It closes the modal. (It is good for user experience)

### Using React Portals to render the Modal

> What🧐 is
>
> [portals](https://react.dev/reference/react-dom/createPortal)
>
>  ?

- Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

> Why🤔 should we use portals?

- Sometimes when we use `overflow` or `z-index` properties in the parent component then we need child component (like modal or dialogues) to break container visually, and portals can be very handy to do that because it renders children outside of DOM hierarchy.

> Syntax✍ for to create portal👇

```javascript
createPortal(children, DOM node - where you want to render this element, key?-A unique string or number to be used as the portal's key)
```

So let's implement portals in our `Modal` component. To use `portal` we have to create one more element in the dom. Generally our whole `App` renders in the div with the id root.

Open `index.html` file and above the root `div` create one more `div` with the id **modal**.
Just like the following code block👇

```javascript
<div id="modal" />
<div id="root" />
```

Let's update the 'Modal.js' file.

```javascript
import { createPortal } from "react-dom";
import "./modal.scss";
import Close from "../times-solid.svg";

const Modal = ({ show, close }) => {
  return createPortal(
    <>
      <div
        className={`modalContainer ${show ? "show" : ""} `}
        onClick={() => close()}
      >
        <div className="modal" onClick={(e) => e.stopPropagation()}>
          <header className="modal_header">
            <h2 className="modal_header-title">{title}</h2>
            <button className="close" onClick={() => close()}>
              <img src={Close} alt="close" />
            </button>
          </header>
          <main className="modal_content">{children}</main>
          <footer className="modal_footer">
            <button className="modal-close" onClick={() => close()}>
              Cancel
            </button>
            <button className="submit">Submit</button>
          </footer>
        </div>
      </div>
    </>,
    document.getElementById("modal")
  );
};

export default Modal;
```

**Line 1**: let's import `createPortal` from `react-dom`.

**Line 6**: After return let's create portal using `createPortal`, As it's first argument we have passed whole modal component and for the second argument we have passed the dom node where we want to render it.

**Line 34**: We want to render our component in the `div` with id **modal**.

### Adding dynamic content to the Modal

open `App.js` file and pass `title` as a props and content inside the component as shown below,

```javascript
<Modal show={modal} title="My Modal" close={Toggle}>
  This is Modal content
</Modal>
```

Here we have passed title as props and modal content as the children. Here is the final code for modal component.

```javascript
import "./modal.scss";
import Close from "../times-solid.svg";
import { createPortal } from "react-dom";

const Modal = ({ show, close, title, children }) => {
  return createPortal(
    <>
      <div
        className={`modalContainer ${show ? "show" : ""} `}
        onClick={() => close()}
      >
        <div className="modal" onClick={(e) => e.stopPropagation()}>
          <header className="modal_header">
            <h2 className="modal_header-title">{title}</h2>
            <button className="close" onClick={() => close()}>
              <img src={Close} alt="close" />
            </button>
          </header>
          <main className="modal_content">{children}</main>
          <footer className="modal_footer">
            <button className="modal-close" onClick={() => close()}>
              Cancel
            </button>
            <button className="submit">Submit</button>
          </footer>
        </div>
      </div>
    </>,
    document.getElementById("modal")
  );
};

export default Modal;
```

**Line 5**: Let's deconstruct `title` and `children` from the props.

**Line 17**: Here, insert title in the curly braces.

**Line 22**: Let's render the children using the curly braces.

Checkout the final code for this tutorial👉 [create Modal using React Portals](https://github.com/codebucks27/React-reusable-components/tree/master/react-modal)

---

## Related on DevDreaming

- [All Blog Posts](https://devdreaming.com/blogs)
- [Free Developer Tools](https://devdreaming.com/tools)
- [Video Tutorials](https://devdreaming.com/videos)
- [AI Tools for Developers](https://devdreaming.com/ai-tools)

---

_This is the Markdown twin of a page on **DevDreaming** -- free developer tutorials, tools, and AI resources. Source of truth: the canonical HTML URL above._