React Portals in 3 Minutes

Jhey Tompkins - Dec 17 '19 - - Dev Community

Grasp this awesome API for escaping DOM restraints whilst creating Portals 🕹😎

What is it?

An API for rendering components outside of your app’s DOM hierarchy.

ReactDOM.createPortal(<Component/>, DOMElement)
Enter fullscreen mode Exit fullscreen mode
Intuitive API 👍

For those in camp TL;DR scroll down for a demo!

Why?

Perfect for scenarios where CSS styles are restraining your elements. For example, stacking(z-index) and overflow issues. You could even render things in a new window! 😎

Link to HackerNoon Article

How?

Instead of returning an element in a component’s render method, return a portal.

const Outsider = () => ReactDom.createPortal(<div>I am outside</div>, document.body)

const App = () => <Outsider/>
Enter fullscreen mode Exit fullscreen mode

Outsider renders as a direct descendant of document.body 👍

When to use?

  • Modals
  • Tooltips
  • Floating menus
  • Widgets

Scope + Bubbling

A brilliant thing about portals is that a component rendered in a portal acts as if it is still in the React tree. It behaves like a normal React child. Pass it props, it will react to updates, etc.

Events fired in a portal will also bubble up through the React tree! Check out the example in the React docs.

Link to React Docs

Basic example (Modal)

Let’s start with a common use case — the Modal. Modals are a great example of where we may need to render a component outside of the current DOM structure.

Our Modal will render based on a state value in the app.

const Modal = ({ children, onClose, open }) =>
  open
    ? ReactDOM.createPortal(
      <div className='modal'>
        <button className='modal__close' onClick={onClose}>&times;</button>
        { children }
      </div>,
      document.body
    )
  : null
Enter fullscreen mode Exit fullscreen mode

For our example, we will render the Modal on document.body. Our Modal is a functional component that accepts children, onClose and open as props.

Here it is in action!

A silly example

Remember the video game "Portal"?

Link to Portal Video Game Wikipedia Article

Let’s create a scene 😅

Let’s start with a Man 🏃. We are going to use Greensock's Draggable to create a draggable Man.

Link to GreenSock documentation for Draggable

Now let’s create a scene with a "Portal". Our man will be bound by the app container.

const App = () => (
  <Fragment>
    <Man bounds={rootNode} />
    <div className="portal portal--in"/>
  </Fragment>
)
Enter fullscreen mode Exit fullscreen mode

That gives us

Draggable Man concept where the Man Component is bound to a container

Now let’s get ReactDOM.createPortal involved 😃

We add an element into the DOM outside of our app (#outside). We also create state for tracking whether our Man is in or out of the app container.

We then use createPortal to render a Portal in #outside. And if outside is true we will use createPortal to render our Man in that outer element 👍

<Man
  bounds={outside ? outsideElement : rootNode}
  onRelease={onRelease}
  outside={outside}
/>
<div className="portal portal--in" ref={innerPortalRef} />
{createPortal(
  <div ref={outerPortalRef} className="portal portal--out" />,
  outsideElement
)}
Enter fullscreen mode Exit fullscreen mode
const ManRender = () => (
  <div className="man" ref={manRef} role="img">
    🏃
  </div>
);

return outside ? createPortal(<ManRender />, bounds) : <ManRender />;
Enter fullscreen mode Exit fullscreen mode

Our Man now invokes an onRelease function too. This checks our cursor position against our portal bounds on release. If we release over a portal, we toggle the state value. All the code is in the demo, there's not much to it 👍

Clip of the working portal. Man component can be dragged in and out of the app container via the fictional portal

If you use your dev tools to inspect the DOM, you’ll see the render happening outside #app 👍

Notes

That’s it!

A 3-minute intro to portals in React!

Link to React Docs on Portals

As always, any questions or suggestions, please feel free to leave a response or tweet me 🐦! I'd love it if you connected with me on the socials 😎

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player