Reactjs Modal Tutorial With React-Portals
Reactjs Modal Tutorial With React-Portals

In this video you will learn what are react-portals and why this is an amazing solution for rendering components in the root level.
Let's jump right into it.

So what is the problem at all? Normally we just render components in React as a tree. But sometimes it doesn't work. If we have things like modals or tooltips normally we want to render them in root level. Why? Because if we just render them inside some component it may be possible that it will be cut because of overflow hidden (if we need it in the component) so it won't be fully visible or we will have positioning problems. To avoid such problems we must render elements standalone in root level. But it's normally not that easy in frontend frameworks because you have this components tree which starts from single element.

And I might say that React solution to this problem in comparison with Angular or Vue is really amazing and simple.

Let's have a look on the real example by building a reusable modal, which can be called from any component but will be rendered in root level.

const App = () => {
  const [isOpened, setIsOpened] = useState(false);

  return (
    <div>
      <h1>Hello monsterlessons</h1>

      <button onClick={() => setIsOpened(true)}>Open Login Modal</button>

      <Modal>
        Modal content
      </Modal>
    </div>
  );
};

So we want to render Modal in App component and it can be any component on any level. Here we render Modal component only after we clicked on the button.
We can also provide any content to our Modal component with children.

Let's add our Modal component now.

import { createPortal } from "react-dom";

const Modal = () => {
  return createPortal(
    <div>
      Modal
    </div>,
    document.getElementById("modal")
  );
};

React gives us super comfortable abstraction with createPortal function. We need to pass as a first argument the markup of our modal component and as second parameter the DOM element where we want to render this component. All difficult things React makes for us and our code looks super clean.

The only thing that we need to do is add in our html file new DOM element where we want to render our modal.

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

As you can see in browser our Modal is already rendered and if we check in elements it's not in our React tree.


Now we want to render our Modal only when is opened property is true. We can handle it outside but it's not nice because then in every place where we want to render a modal we must wrap it with if condition. For me it's much easier to pass it in modal component directly.

<Modal isOpened={isOpened}>Modal conent</Modal>

const Modal = ({ isOpened }) => {
  if (!isOpened) {
    return null;
  }
  return createPortal(<div>Modal</div>, document.getElementById("modal"));
};

Now it already works. We see our modal only after we clicked on the button.

Let's add some styles to make it look good.

Modal.js

import "./modal.css";
...
return createPortal(
  <div>
    <div className="overlay"></div>
    <div className="modal">
      <div>
        <span className="close-button">
          X
        </span>
        <div className="modal-content">{children}</div>
      </div>
    </div>
  </div>,
  document.getElementById("modal")
);

modal.css

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #fff;
  width: 500px;
  height: 300px;
  padding: 10px;
  z-index: 3
}

.overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, .7);
  z-index: 2
}

.close-button {
  position: absolute;
  right: 20px;
  top: 15px;
  cursor: pointer;
}

.modal-content {
  margin-top: 40px;
}

It already looks really good. The only thing that we are missing is close event on click.

Modal.js

const Modal = ({ isOpened, children, onClose }) => {
  ...
}

<span onClick={onClose} className="close-button">

App.js

<Modal isOpened={isOpened} onClose={() => setIsOpened(false)}>

Now our close button works correctly.

Our modal is fully functional and reusable.


Let's provide some specific markup from our App component. For example for sign up form.

<Modal isOpened={isOpened} onClose={() => setIsOpened(false)}>
  <form onSubmit={() => setIsOpened(false)} className="login-form">
    <section>
      <label>Email</label>
      <input type="email" />
    </section>
    <section>
      <label>Password</label>
      <input type="password" />
    </section>
    <button>Sign In</button>
  </form>
</Modal>

app.css

label {
  display: block;
  font-size: 18px;
}

input {
    width: 300px;
    height: 30px;
    font-size: 30px;
}

section {
  margin-bottom: 10px;
}

.login-form {
    width: 300px;
    margin: 0 auto;
    margin-top: 70px;
}

Our sign up form is ready and we used our sharable Modal which is rendered in root for this.

As you can see React did amazing job in hiding complexity of rendering elements in root of html and providing us only simple API of createPortal which allowed us to pass props and events in the same way as always and build sharable Modal component in a matter of minutes.

Also if you want to improve your programming skill I have lots of full courses regarding different web technologies.

📚 Source code of what we've done