{"id":280606,"date":"2019-01-14T16:22:41","date_gmt":"2019-01-14T23:22:41","guid":{"rendered":"http:\/\/css-tricks.com\/?p=280606"},"modified":"2019-01-14T16:22:41","modified_gmt":"2019-01-14T23:22:41","slug":"using-react-portals-to-render-children-outside-the-dom-hierarchy","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/using-react-portals-to-render-children-outside-the-dom-hierarchy\/","title":{"rendered":"Using React Portals to Render Children Outside the DOM Hierarchy"},"content":{"rendered":"

Say we need to render a child element into a React application. Easy right? That child is mounted to the nearest DOM element and rendered inside of it as a result.<\/p>\n

render() {\r\n  return (\r\n    <div>\r\n      \/\/ Child to render inside of the div\r\n    <\/div>\r\n  );\r\n}<\/code><\/pre>\n

But! What if we want to render that child outside<\/em> of the div somewhere else? That could be tricky because it breaks the convention that a component needs to render as a new element and follow a parent-child hierarchy. The parent wants to go where its child goes.<\/p>\n

That\u2019s where React Portals<\/a> come in. They provide a way to render elements outside the DOM hierarchy so that elements are a little more portable. It may not be a perfect analogy, but Portals are sort of like the pipes in Mario Bros. that transport you from the normal flow of the game and into a different region.<\/p>\n

<\/p>\n

\"\"<\/figure>\n

The cool thing about Portals? Even though they trigger their own events that are independent of the child\u2019s parent element, the parent is still listening to those events, which can be useful for passing events across an app.<\/p>\n

We\u2019re going to create a Portal together in this post then make it into a re-usable component. Let\u2019s go!<\/p>\n

The example we\u2019re building<\/h3>\n

Here\u2019s a relatively simple example of a Portal in action:<\/p>\n

See the Pen React Portal<\/a> by Kingsley Silas Chijioke (@kinsomicrote<\/a>) on CodePen<\/a>.<\/p>\n

Toggling an element\u2019s visibility is nothing new. But, if you look at the code carefully, you\u2019ll notice that the outputted element is controlled by the button even though it is not a direct descendent of it. In fact, if you compare the source code to the rendered output in DevTools, you\u2019ll see the relationship:<\/p>\n

\"\"<\/figure>\n

So the outputted element\u2019s parent actually listens for the button click event and allows the child to be inserted even though it and the button are separate siblings in the DOM. Let\u2019s break down the steps for creating this toggled Portal element to see how it all works.<\/p>\n

Step 1: Create the Portal element<\/h3>\n

The first line of a React application will tell you that an App element is rendered on the document root using ReactDOM. Like this;<\/p>\n

ReactDOM.render(<App \/>, document.getElementById(\"root\"));<\/code><\/pre>\n

We need to place the App element in an HTML file to execute it:<\/p>\n

<div id=\"App\"><\/div><\/code><\/pre>\n

Same sort of thing with Portals. First thing to creating a Portal is to create a new div element in the HTML file.<\/p>\n

<div id=\"portal\"><\/div><\/code><\/pre>\n

This div will serve as our target. We\u2019re using #portal<\/code> as the ID, but it doesn\u2019t have to be that. Any component that gets rendered inside this target div will maintain React\u2019s context. We need to store the div as the value of a variable so we can make use of the Portal component that we\u2019ll create:<\/p>\n

const portalRoot = document.getElementById(\"portal\");<\/code><\/pre>\n

Looks a lot like the method to execute the App element, right?<\/p>\n

Step 2: Create a Portal component<\/h3>\n

Next, let\u2019s set up the Portal as a component:<\/p>\n

class Portal extends React.Component {\r\n  constructor() {\r\n    super();\r\n    \/\/ 1: Create a new div that wraps the component\r\n    this.el = document.createElement(\"div\");\r\n  }\r\n  \/\/ 2: Append the element to the DOM when it mounts\r\n  componentDidMount = () => {\r\n    portalRoot.appendChild(this.el);\r\n  };\r\n  \/\/ 3: Remove the element when it unmounts\r\n  componentWillUnmount = () => {\r\n    portalRoot.removeChild(this.el);\r\n  };\r\n  render() {\r\n    \/\/ 4: Render the element's children in a Portal\r\n    const { children } = this.props;\r\n    return ReactDOM.createPortal(children, this.el);\r\n  }\r\n}<\/code><\/pre>\n

Let\u2019s step back and take a look at what is happening here.<\/p>\n

We create a new div element in the constructor and set it as a value to this.el<\/code>. When the Portal component mounts, this.el<\/code> is appended as a child to that div in the HTML file where we added it. That\u2019s the <div id=\"portal\"><\/div><\/code> line in our case.<\/p>\n

The DOM tree will look like this.<\/p>\n

<div> \/\/ Portal, which is also portalRoot\r\n  <div> \/\/ this.el\r\n  <\/div>\r\n<\/div><\/code><\/pre>\n

If you\u2019re new to React and are confused by the concept of mounting and unmounting an element, Jake Trent has a good explanation<\/a>. TL;DR: Mounting is the moment the element is inserted into the DOM.<\/p>\n

When the component unmounts we want to remove the child to avoid any memory leakage. We will import this Portal component into another component where it gets used, which is the the div that contains the header and button in our example. In doing so, we\u2019ll pass the children elements of the Portal component along with it. This is why we have this.props.children<\/code>.<\/p>\n

Step 3: Using the Portal<\/h3>\n

To render the Portal component\u2019s children, we make use of ReactDOM.createPortal()<\/code>. This is a special ReactDOM method that accepts the children and the element we created. To see how the Portal works, let\u2019s make use of it in our App component.<\/p>\n

But, before we do that, let\u2019s cover the basics of how we want the App to function. When the App loads, we want to display a text and a button \u2014 we can then toggle the button to either show or hide the Portal component.<\/p>\n

class App extends React.Component {\r\n  \/\/ The initial toggle state is false so the Portal element is out of view\r\n  state = {\r\n    on: false\r\n  };\r\n\r\n  toggle = () => {\r\n    \/\/ Create a new \"on\" state to mount the Portal component via the button\r\n    this.setState({\r\n      on: !this.state.on\r\n    });\r\n  };\r\n  \/\/ Now, let's render the components\r\n  render() {\r\n    const { on } = this.state;\r\n    return (\r\n      \/\/ The div where that uses the Portal component child\r\n      <div>\r\n        <header>\r\n          <h1>Welcome to React<\/h1>\r\n        <\/header>\r\n        <React.Fragment>\r\n          \/\/ The button that toggles the Portal component state\r\n          \/\/ The Portal parent is listening for the event\r\n          <button onClick={this.toggle}>Toggle Portal<\/button>\r\n          \/\/ Mount or unmount the Portal on button click\r\n          <Portal>\r\n            {\r\n              on ?\r\n                <h1>This is a portal!<\/h1>\r\n              : null\r\n            }\r\n          <\/Portal>\r\n        <\/React.Fragment>\r\n      <\/div>\r\n    );\r\n  }\r\n}<\/code><\/pre>\n

Since we want to toggle the Portal on and off, we need to make use of component state<\/a> to manage the toggling. That\u2019s basically a method to set a state of on<\/code> to either true<\/code> or false<\/code> on the click event. The portal gets rendered when on<\/code> is true; else we render nothing.<\/p>\n

This is how the DOM looks like when the on<\/code> state is set to true<\/code>.<\/p>\n

\"\"<\/figure>\n

When on<\/code> is false<\/code>, the Portal component is not being rendered in the root, so the DOM looks like this.<\/p>\n

\"\"<\/figure>\n

More use cases<\/h3>\n

Modals are a perfect candidate for Portals. In fact, the React docs use it as the primary example for how Portals work:<\/p>\n

See the Pen Example: Portals<\/a> by Dan Abramov (@gaearon<\/a>) on CodePen<\/a>.<\/p>\n

It\u2019s the same concept, where a Portal component is created and a state is used to append the its child elements to the Modal component. <\/p>\n

We can even insert data from an outside source into a modal. In this example, the App component lists users fetched from an API using axios<\/a>.<\/p>\n

See the Pen React Portal 3<\/a> by Kingsley Silas Chijioke (@kinsomicrote<\/a>) on CodePen<\/a>.<\/p>\n

How about tooltips? David Gilberston has a nice demo:<\/p>\n

See the Pen React Portal Tooptip<\/a> by David Gilbertson (@davidgilbertson<\/a>) on CodePen<\/a>.<\/p>\n

J Scott Smith shows how Portals can be used to escape positioning:<\/p>\n