What are React Portals

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: