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.
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
! 😎
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/>
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.
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}>×</button>
{ children }
</div>,
document.body
)
: null
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"?
Let’s create a scene 😅
Let’s start with a Man
🏃. We are going to use Greensock
's Draggable
to create a draggable Man
.
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>
)
That gives us
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
)}
const ManRender = () => (
<div className="man" ref={manRef} role="img">
🏃
</div>
);
return outside ? createPortal(<ManRender />, bounds) : <ManRender />;
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 👍
If you use your dev tools to inspect the DOM, you’ll see the render happening outside #app
👍
Notes
- Don’t neglect Accessibility! Maintaining keyboard focus etc. is very important.
- Available in React@16.0.0+
That’s it!
A 3-minute intro to portals in React!
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 😎
Top comments (6)
I read articles about using react portals, but never really felt the need to use it itself.... until now. Modals appear to be the perfect use-case, I've been giving far too high zIndexs to my modals :)
Thank you immensely for the post Jhey. It has truly widened my perspective on coding with React.
Hey joon 👋
No problem! I'm glad I could help 😊We can do some pretty cool things with Portals 😎
I am so glad you used an example from the portal game here. Perfect.
I felt obliged 😅I'm glad it's appreciated!
Really awesome introduction to Portals, I feel the need to try it.
Thanks Ronald! 😊I appreciate that. Try it out, it's quite cool what opportunities it provides.