Thinking With Portals
a post about React Portals
Have you ever wanted to render children into a DOM node that exists outside of the DOM hierarchy of the parent component? Have you ever tried to create a modal but it messed up all of your CSS? Don’t even know what I’m talking about? Been there.
Conventionally, when we render a React component, it’s mounted to the DOM as a child of the nearest parent node and restrained to the DOM tree we call render()
in. Helicopter Parent much?
However, with the addition of Portals in React 16, children components are now able to “break away” from their pesky parents. But, they’re not completely free; components rendered through Portals retain the behaviors of a normal React child.
This is because the children and the portals exist in the React tree regardless of their position in the DOM tree. Learn more about this in the React docs.
Wait, but why?
Because of how the DOM is structured in React (<App />
as a child of <div id="root">
) our components inevitably become deeply nested, more so as the size of our React App increases. If we try to create a pop-up (or similar) in one of these nested components, we’re going to run into some CSS problems, namely with z-index and overflow.
Portals give us the capacity to render components under a different DOM, or even in a different window. Using Portals, we no longer need to worry about how our pop-up (and the rest of our app) might be affected by CSS restraints.
Ok, so how?
Let’s run through how we’d go about adding a tooltip to our app.
1. Creating a reusable Portal component:
- in our
constructor()
, we wrap our component in a<div>
. Then, we usecomponentDidMount
andcomponentWillUnmount
to slap it on the DOM and remove it from the DOM appropriately. - in our
render()
, we callReactDOM.createPortal()
, which takes an argument of JSX going into the portal, and the target destination. - note that our
render()
isn’t actually returning anything. Rather, we’re setting up this component to do something elsewhere.
2. Creating a reusable Tooltip component:
- we initiate the component with a boolean and write event handlers to toggle the tooltip via mouse over
- on mouse over the portal is “opened” and the tooltip is displayed, otherwise the portal is “closed” and the tooltip is hidden
this.props.children
andthis.props.text
are defined when we use the component below:
In CodePen:
With some extra CSS, we could make the tooltip display elsewhere. We could even modify our Portal component to open our tooltip in new windows on each mouse over.
Wrapping up:
- portals allow us to insert a child anywhere in the DOM tree and visually “escape” a parent container (eg. modals, tooltips, popups)
- a component rendered using portals behaves like a normal React child because it still exists within the React tree
Important note on accessibility: managing keyboard focus for portals in general, and following modal authoring practices when creating modals.