Table of contents
1.
Introduction
2.
What are portals in react?
2.1.
index.html
2.2.
index.js
3.
Features
4.
When to use React Portals?
5.
How to implement portals in react?
5.1.
Index.html
5.2.
PortalDemo.js
5.3.
App.js
5.4.
PortalDemo.js
6.
Why do we need portals?
6.1.
Example
6.1.1.
Index.js
7.
Event Bubbling through portals.
7.1.
Example
7.1.1.
Index.js
8.
Frequently Asked Questions
8.1.
What are the Portals in React?
8.2.
What is the purpose of React Portals?
8.3.
How do you make a Portal in React?
8.4.
What is the best use case for using React Portals?
9.
Conclusion
Last Updated: Mar 27, 2024
Medium

React Portals

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

React is a component-based language, meaning we can have different components coming together to display a webpage. 

This component-based architecture allows us to reuse our code in various components with the help of render props in react.

For a brief understanding, Portals provide a first-class way to render children into a DOM node outside the parent component’s DOM hierarchy.

Further, we will be discussing one interesting topic in React, i.e., portals in react. 

Now let us get into the concept of portals in react.

Portals in React

What are portals in react?

Portals in react provide a way to render children into a DOM node that exists outside the hierarchy of the parent component.

The react version 16.0 introduced the portals in react.

Basically, We have had one DOM element in our HTML on which we were mounting our react app. 

Looking at the index.html file, we can see that element is the element with id equal to “root.” 

index.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" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>React App</title>

  </head>

  <body>

    <noscript>You need to enable JavaScript to run this app.</noscript>

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

  </body>

</html>

 

 

In the index.js file, we use reactDOM.render and mount our app component onto the root element.

index.js

import React from 'react';

import ReactDOM from 'react-dom';

import './index.css';

import App from './App';

import reportWebVitals from './reportWebVitals';

ReactDOM.render(

  <React.StrictMode>

    <App />

  </React.StrictMode>,

  document.getElementById('root')

);

reportWebVitals();

 

 

In the browser, in the element DOM tree, we can see that every single component in our react application falls under the root element, the div element with id equals to root.

 

element DOM tree

 

The react portals allow us to break out of this DOM tree; we can render a component in the DOM node that is not under this root element.

Features

Here are some feature of React Portals:

  • Render children outside of the parent DOM hierarchy.
  • Support with event bubbling and capturing.
  • Can be used with any component, not just modals.
  • Compatible with server processing.
  • Makes it easier to create accessible UI components.
  • Can increase performance by removing unnecessary rendering.
  • Increases the freedom of UI design.

When to use React Portals?

React Portals are used when you need to display a component outside of its parent DOM hierarchy, such as for modal dialogs, popovers, and tooltips. This provides for more flexibility in the UI design and prevents issues with styling or layout. Portals can also improve speed by reducing unnecessary rendering and DOM manipulation, making them a useful tool in any React application.

How to implement portals in react?

Firstly we will add a DOM node that is outside of the root element.

In index.html, right below the root element, we add a new div tag with id equals portal-root.

Index.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" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>React App</title>

  </head>

  <body>

    <noscript>You need to enable JavaScript to run this app.</noscript>

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

    <div id="portal-root"></div>

  </body>

</html>

 

 

Secondly, We will create a new Component, namely, PortalDemo.js; within the file, we will create a functional component.

In the JSX, we will add a simple heading tag.

PortalDemo.js

import React from "react";

 

function PortalDemo() {

  return <h1>Portals in react</h1>;

}

export default PortalDemo;

 

Then, we will include the component in the app component. 

App.js

import React from "react";

import "./App.css";

import PortalDemo from "./components/PortalDemo";

function App() {

  return (

    <div classname="App">

      <PortalDemo />

    </div>

  );

}

export default App;

 

 

After saving all the files and having a look at the browser looking in the DOM tree, we can see that on inspecting the heading, we see that the heading element falls under the root element and not under the “portal-root” element.

 

portal-root

 

Let us change that,

We will use ReactDOM.createPortal method to insert this component under the portal-root node.

So in the PortalDemo component at the top, we need to import the ReactDOM package,

Then in the render method, instead of simply returning the JSX, we will return ReactDOM.createPortal. 

This method takes two arguments; the first one is the JSX you want to render; over here, it is the heading, and the second parameter is the DOM node on which we want to mount the component.

PortalDemo.js

import React from "react";

import ReactDOM from "react-dom";

function PortalDemo() {

  return ReactDOM.createPortal(

    <h1>Portals in react</h1>,

    document.getElementById("portal-root")

  );

}

export default PortalDemo;

 

 After saving all the files, we still see the heading “Portals in react.” 

When we inspect the element, we see that the heading tag is inside the portal-root DOM node.

DOM node

So in the react application, even though all the components are children to the app component, and the app component is mounted on the root DOM node, it is possible to break away from that and to mount on any DOM node that you wish to use by the help of React portals.

Note: The first parameter to create a portal can be any element that react can render. It can be numbers, strings, JSX, or even components.

Why do we need portals?

One of the use cases, which is brought up, is having to deal with the parent component CSS when the child component is a model, pop-up, or tooltip.

Example

First, we will see how the code works with the portals.

In the index.html, we have two div tags, one with id “root” and the other “modal-root.”

modal-root

In index.js, we have references to both elements. We have a Modal component.

Index.js

import React from 'react'

import ReactDOM from 'react-dom'

 

const root = document.getElementById('root')

const modalRoot = document.getElementById('modal-root')

 

class Modal extends React.Component {

 render() {

   return ReactDOM.createPortal(

     <div

       style={{

         position: 'absolute',

         top: '0',

         bottom: '0',

         left: '0',

         right: '0',

         display: 'grid',

         justifyContent: 'center',

         alignItems: 'center',

         backgroundColor: 'rgba(0,0,0,0.2)',

       }}

       onClick={this.props.onClose}

     >

       <div

         style={{

           padding: 20,

           background: '#fff',

           borderRadius: '2px',

           display: 'inline-block',

           minHeight: '300px',

           margin: '1rem',

           position: 'relative',

           minWidth: '300px',

           boxShadow: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)',

           justifySelf: 'center',

         }}

       >

         {this.props.children}

         <hr />

         <button onClick={this.props.onClose}>Close</button>

       </div>

     </div>,

     modalRoot,

   )

 }

}

 

class App extends React.Component {

 state = {showModal: false}

 handleShowMessageClick = () => this.setState({showModal: true})

 handleCloseModal = () => this.setState({showModal: false})

 render() {

   return (

     <div

       style={{

         height: '100%',

         display: 'grid',

         justifyContent: 'center',

         alignItems: 'center',

       }}

     >

       <div

         style={{

           maxWidth: 400,

           position: 'relative',

         }}

       >

         <h1>My App</h1>

         <p>

           This is an example of how you might use React.createPortal. I think

           it is a pretty neat API that is yet another awesome escape hatch

           that React provides for practical reasons. Sometimes you just need

           to render something completely outside the React Tree.

         </p>

         <button onClick={this.handleShowMessageClick}>

           Show Secret Modal

         </button>

         {this.state.showModal ? (

           <Modal onClose={this.handleCloseModal}>

             This is the secret modal message!

           </Modal>

         ) : null}

       </div>

     </div>

   )

 }

}

 

ReactDOM.render(<App />, root)

 

 

Output:
 

Modal component from the index.js

Now, we remove the react portal from the Modal component from the index.js file by removing the method ReactDOM.createPortal and its second parameter. Modifying it to as shown below:

class Modal extends React.Component {

 render() {

   return (

     <div

       style={{

         position: 'absolute',

         top: '0',

         bottom: '0',

         left: '0',

         right: '0',

         display: 'grid',

         justifyContent: 'center',

         alignItems: 'center',

         backgroundColor: 'rgba(0,0,0,0.2)',

       }}

       onClick={this.props.onClose}

     >

       <div

         style={{

           padding: 20,

           background: '#fff',

           borderRadius: '2px',

           display: 'inline-block',

           minHeight: '300px',

           margin: '1rem',

           position: 'relative',

           minWidth: '300px',

           boxShadow: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)',

           justifySelf: 'center',

         }}

       >

         {this.props.children}

         <hr />

         <button onClick={this.props.onClose}>Close</button>

       </div>

     </div>

   )

 }

}

 

 

The output after that will be as shown below.

Output:

distorted output

We can see that without portals, we can see that the output is distorted, and the model does not work the way we want it.

It is because the CSS of the parent is getting used by the model, which is not desirable. We want to have different styling for the model component that is achieved by the portals in react.
 

Click on the following link to read further: Hooks in React JS

Event Bubbling through portals.

Even though a portal can be anywhere in the DOM tree, it behaves like a normal React child in every other way. 

An event fired from inside the portal will propagate to the ancestor into containing react tree, even if those elements are not the ancestors in the DOM tree. This phenomenon is known as event bubbling

Example

Assume the following structure

<div id="app-root"></div>

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

 

 

A Parent component in the div with id as app-root would catch an uncaught, bubbling event from the sibling node in the div with id as modal-root.

Index.js

const appRoot = document.getElementById('app-root');

const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {

  constructor(props) {

    super(props);

    this.ele = document.createElement('div');

  }

  render() {

    return ReactDOM.createPortal(

      this.props.children,

      this.ele,

    );

  }

}

class Parent extends React.Component {

  constructor(props) {

    super(props);

    this.state = {clicks0};

    this.manageClick = this.manageClick.bind(this);

  }

  manageClick() {

    // This will fire when the button in Child is clicked,

    // updating Parent's state, even though button

    // is not a direct descendant in the DOM.

    this.setState(prevState => ({

      clicksprevState.clicks + 1

    }));

  }

 

  render() {

    return (

      <div onClick={this.manageClick}>

        <p>Number of clicks: {this.state.clicks}</p>

        <p>

          Open up the browser DevTools

          to observe that the button

          is not a child of the div

          with the onClick handler.

        </p>

        <Modal>

          <Child />

        </Modal>

      </div>

    );

  }

}

function Child() {

  // The click event on this button will bubble up to the parent,

  // because there is no 'onClick' attribute defined

  return (

    <div className="modal">

      <button>Click</button>

    </div>

  );

}

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


Try it yourself on Codepen.

different DOM tree

The click event is passed through the child component, which is propagated to the parent component despite being in a different DOM tree. However, they are in the same React tree.

Catching an event bubbling up from a portal in a parent component allows the development of more flexible abstractions that are not inherently reliant on portals. For example, if you render a <Modal /> component, the parent can capture its events regardless of whether it’s implemented using portals.

Check out Advantages of React JS here.

Frequently Asked Questions

What are the Portals in React?

Portals in React allow you to display a child component outside of its parent DOM hierarchy. This is helpful when creating UI elements such as modal dialogues or popovers that must be placed outside of the parent element.

What is the purpose of React Portals?

The purpose of React Portals is to provide a method to render a child component outside of its parent DOM hierarchy. This enables for more flexible UI design, particularly when creating modal dialogs, popovers, and tooltips.

How do you make a Portal in React?

To make a Portal in React, first create a new container element outside of the root element in your HTML file. Then, using ReactDOM.createPortal(), you can display your component into this new container element.

What is the best use case for using React Portals?

React Portals are best used for displaying UI components that must be placed outside of their parent component, such as modal dialogues or popovers. This can help in avoiding style and layout issues and allowing for more adaptable UI design.

Conclusion

We learned about the concept of portals in react. This is an advanced concept of portals in React that allows us to shift a react component to another DOM node. This article also explained why there is a need for portals in react specifically. We also learned about the implementation of portals in react. You can also consider our React Js Course to give your career an edge over others.

Recommended Readings:

 

Live masterclass