What are React Portals

June 16, 2020

Image from instagram

What is a React Portal?

Available in React 16, it allows your component to render outside your app’s DOM heiarchy.

From the official React documentation:

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

Create a portal

Normally in a React app you have your index.html somthing like this:

<html>
    <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
    </body>
</html>

And your entire app runs inside this Dom element div#root.

But with portals, you can have multile DOM elements and can render your components in a different DOM element when needed.
Your index.html will look something like this:

<html>
    <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
        <div id="my-portal"></div>
    </body>
</html>

Now you can have some of your components render in div#my-portal.

When to use portals?

  • To show Dialogs/Modals
  • Tooltips
  • Menus
  • Other full-screen widgets

How to render a component in a portal

In this example we’ll create a full screen dialog and show it in a portal.

In our App.js:

import React, {useState} from 'react';
import logo from './logo.svg';
import './App.css';
import Dialog from './Dialog'

function App() {
  const [open, setOpen] = useState(false);
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <div
          className="App-link"
          onClick={()=>setOpen(!open)}
        >
          Open Dialog
        </div>
      </header>
      <Dialog isOpen={open} onClose={()=>setOpen(!open)}/>
    </div>
  );
}

export default App;

Dialog is our component which we’ll render in a portal.
We’re using useState to manage the state of our dialog and pass an onClose event as a prop.

In our Dialog.js:

import React from 'react';
import logo from './logo.svg';
import './dialog.css';
import { createPortal } from 'react-dom';

function Dialog({isOpen, onClose}) {
    return isOpen?
  createPortal (
    <div className="Dialog">
      <header className="Dialog-header" >
        <img src={logo} className="Dialog-logo" alt="logo" />
        <p>
          I'm a Dialog!
        </p>
        <button
          onClick={onClose}
        >
          Close dialog
        </button>
      </header>
    </div>
  , document.getElementById("my-portal")) :null
}

export default Dialog;
  • We import createPortal from ‘react-dom’
  • We render null if the isOpen prop is false
  • If isOpen is true, we return the JSX containing our dialog.

ReactDOM.createPortal(child, container)

From React Documentation:
The first argument (child) is any renderable React child, such as an element, string, or fragment. The second argument (container) is a DOM element.


Resources:


Written by Gagandeep Rangi who likes to talk about himself in third person. Twitter Instagram

Email icon