Feel free to jump straight to my CodePen example. This post also appears on my personal blog.
When developing UI with components, the tendency is to think from the outside in. Especially with React, it almost always starts with an app shell component, page components, and then smaller components. This can make it challenging when you have to build elements that "pop out" of this paradigm. Where do you place these components?
React Portals create a simple solution to this problem. It allows you to think "outside" your app and render components outside your app shell, but still have those components share state with your app.
In this tutorial, I'll walk you through building a simple notification component that you can use as the basis of any notification, modal, or chat bubble style UI element.
The first thing you need to do is make a new HTML element in your app in which your notification will be rendered.
<div id="root">
<!-- This is your app root. You have this (maybe with a different id) already. -->
</div>
<div id="portal">
<!-- This is new! It's where your new notification element will be rendered. -->
</div>
The second thing you need to do is create a container element for your notification. This is the cool part! This is the element you declare inside your app that is rendered outside the app root.
class NotificationContainer extends React.Component {
render() {
// grab the new element you declared above
const domElement = document.getElementById('portal');
return ReactDOM.createPortal(
this.props.children,
domElement
);
}
}
Now your notification container needs some content inside. Let's make a component for that.
class Notification extends React.Component {
render() {
return (
<div> The world says hello! </div>
)
}
}
Alright, we have all the new necessary components, so let's actually use them.
// this is the app root that's being rendered inside that div with id="root".
class App extends React.Component {
render() {
return (
<div>
<h1>Hello world!</h1>
<NotificationContainer>
<Notification/>
</NotificationContainer>
</div>
);
}
}
Congrats, you just made a notification! Of course, you will probably want to throw some styling on it but the basics are there.
Here are some cool benefits to building notifications this way:
- You can use the state of your app to control the notification. This can let you do things like:
- delay the notification / have it on a timer.
- swap out the child of the NotificationContainer so you can render different components
- display information from your app state in your easily
- This simplifies CSS a little bit. It can be annoying to wrangle the CSS of notifications from inside your app, this makes it easier by bringing the div to the same level as your app root.
If you want to see an example of using state to delay the notification pop up, here is my CodePen that builds off this example.
Top comments (6)
Nice post! Btw you can add syntax highlighting to your codeblocks by adding a keyword (usually the language) after the first set of triple backticks. For example:
Oh sweet! I was looking for this in Help when writing the post but didn't see it. Thank you.
Ah good to know. We should probably add that in there.
Thanks for sharing! I'm actually in the midst of creating a similar article about React.Portal for a modal I recently created so your post was very insightful! One thing I noticed with React.Portal is that the div id="portal" always seems to exist in the DOM which slightly bothered me. I've been tinkering around with appending the div to the body on componentDidMount of Modal component-- not sure if that's considered bad practice but I'm having fun trying it out. Hopefully I'll have more to share soon after some experimentation :)
Does it work with SSR ?
I've never tried it but according to some articles I've read online it's possible. Here's an article I found where the author made it work!