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.