Learn React Portal and Its Applications

Bernard Bado
November 30, 2021
Learn React Portal and Its Applications
When you use affiliate links in this article, We earn a small comission. Thank you!
note

We wrote more up-to-date version of this article. You can read article by using this link.

React Portals have made it easier to create floating modals. It provides an escape from monotonous hierarchy with the help of Portals. But at the same time, React Portals do not disturb event propagation. The parent component is listening to those events; thus, making it more feasible to handle the events across an application.

React Portal is a great way to render floating components. React Portal renders child components into a DOM node without disturbing event propagation and provides greater flexibility outside of the hierarchy of a parent container.

info

React Portals have made it easier to create floating modals. It provides an escape from monotonous hierarchy with the help of Portals. But at the same time, React Portals do not disturb event propagation. The parent component is listening to those events; thus, making it more feasible to handle the events across an application.

Let’s look at the creation of React Portal in this article.

Example of React Portal

The syntax of React Portal is:

ReactDOM.createPortal(child, container)

In this syntax, the first argument is the React child component. It could be a string or a fragment, or an element. The second argument is the DOM element.

To create a new DOM node, navigate to the public/index.htmlfile and type:

<div id="root"></div> <!-- this is root node -->
<div id="portal-root"></div> <!-- this node portal node -->

Now, to implement React Portal, create a js file. Let’s name it Modal.js. In this file, write the code to create a modal.

Modal.jsx
import ReactDOM from "react-dom";

export default function Modal() {
  const portalDOM = document.getElementById("portal-root");
  return ReactDOM.createPortal(<div>Modal Portal</div>, portalDOM);
}

Now, add the modal in the App.js file of the React application.

App.jsx
import React from "react";
import "./App.css";
import Modal from "./components/modal";

class App extends React.Component {
  render() {
    return (
      <div>
        <Modal />
      </div>
    );
  }
}
note

In this example, the App.jsfile renders the modal that you created. If you inspect the code using the browser dev tool, you will see that it is evident that the modal has its divelement.

Common Use Cases of React Portals

The most common use cases for React Portals are tooltips, dialog boxes, hover cards, modals, or loaders. You can also use React Portals when the child component should be visually separate from the parent container. It is even more useful when a parent component has overflow: hidden or z-index styling settings.

Whatever the use case may be, there are some points that you must keep in mind while working with React Portals:

  • Events will be propagating to the React tree ancestors.
  • React has control over the child component’s lifecycle even when using the Portals.
  • Portals affect the HTML DOM structure and not the React tree.
  • You need to define an HTML DOM element as a mount point for the Portal’s component.

How to Use React Portal in React — A Modal Example

One of the most common use cases is Modals in React. Lots of applications use Modals because it improves the overall UX of the application. Furthermore, they are useful in grabbing the user’s attention. React official documentation also uses Modals as an example to demonstrate how the models work perfectly.

caution

Before you start with the modal implementation, make sure that React is updated to the latest version.

Like the sample example shown above, add the modal div above root div.

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

Now, create a Modal component and name it Modal.js. This component will act like a modal, and you can put content inside it.

Modal.jsx
import React from "react";
import { createPortal } from "react-dom";

const modalRoot = document.getElementById("modal");

class Modal extends React.Component {
  constructor(props) {
    super(props);
    // Create div for this modal
    this.element = document.createElement("div");
  }
  // Append div to the div#modal
  componentDidMount() {
    modalRoot.appendChild(this.element);
  }
  /**
   * Make sure to remove the div when it is closed, and save the    memory.
   */

  componentWillUnmount() {
    modalRoot.removeChild(this.element);
  }
  render() {
    return createPortal(this.props.children, this.element);
  }
}
export default Modal;

Add CSS to your file as per your requirements to set the layout. In the next step, import this Modal into any of your components.

Home.jsx
import React from "react";
import Modal from "./Modal";

export default class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showModal: false,
    };
  }
  toggleModal = () => {
    this.setState({
      showModal: !this.state.showModal,
    });
  };
  render() {
    const { showModal } = this.state;
    return (
      <React.Fragment>
        <button className="modal-toggle-button" onClick={this.toggleModal}>
          {!showModal ? "Open Modal" : "Close Modal"}
        </button>
        {showModal ? (
          <Modal>
            <h1>Sample Heading</h1>
            <p>Sample Paragraph</p>
            <button className="modal-close" onClick={this.toggleModal}>
              X
            </button>
          </Modal>
        ) : null}
      </React.Fragment>
    );
  }
}
note

In this code, we’ve set the showModalproperty to false. The Modal will be visible at the click of the button.

Event Bubbling in React Portals

Although you can create a React Portal anywhere in the document, it is still under the React tree, regardless of its position in the DOM hierarchy. The same happens when the child component propagates the event. The event fired from the child propagates to its ancestors in the React tree, even if they do not exist in the DOM hierarchy.

Let’s look at an example of event bubbling. For instance, you have this HTML structure:

<html>
<body>
<div id=”app-root”></div>
<div id=”modal-root”></div>
</body>
</html>

A parent component in #app-root will be able to catch a bubbling event from its sibling node #modal-root as both are sibling containers in the DOM.

Modal.jsx
const appRoot = document.getElementById("app-root");
const modalRoot = document.getElementById("modal-root");

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement("div");
  }
}
info

The Modal’s children are mounted on a detached DOM node. If there is a need to attach a child component to the DOM tree, add the state to the Modal and only render the child component.

Parent.jsx
import React from "react";

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { clicks: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }
  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }
  render() {
    return ReactDOM.createPortal(this.props.children, this.el);
  }
}
note

The event handleClick() will fire when you click the button in Child. Keep in mind that the button is not the immediate descendant in the DOM hierarchy.

handleClick() {
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
}

render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <Modal>
           <Child />
        </Modal>
      </div>
    );
}

A click on the button will trigger an event that will bubble up to the parent as we have not defined any onClickattribute.

Child.jsx
function Child () {
    return (
      <div className="modal">
       <button>Click</button>
      </div>
    );
}

ReactDOM.render(<Parent />, appRoot);

Concluding Thoughts

In this article, you learned about React Portal in React Application. You learned use cases of React portals. And you also learned about event bubbling in React portals.

info

The concept is easy, however, it provides the flexibility to handle reusable components outside the DOM hierarchy without breaking the default event behavior.