React Portals
By Flavio Copes
Learn how React Portals render an element outside its component hierarchy with createPortal from react-dom, a perfect fit for modal windows styled at full screen.
Portals have been part of React since version 16 (September 2017). They are still the same idea in React 19: render a piece of UI outside the normal parent DOM node, while React keeps handling events through the component tree.
A portal is a way to render an element outside of its component hierarchy, in a separate DOM node.
When that event is rendered, events happening on it are managed by the React components hierarchy rather than by the hierarchy set by the DOM position of the element.
Hence the name “portal”: an element sits somewhere in the DOM tree that’s outside of the normal React components tree, but the React component tree that includes it is still in charge.
React offers an easy API to do this, createPortal() from react-dom, which accepts 2 arguments. The first is the element to render, the second is the DOM element where to render it.
A classic use case for this is modal windows.
A modal to render at full screen must live outside of the element, so it can be properly styled using CSS.
So if a modal is defined as a component:
import { createPortal } from 'react-dom'
class Modal extends React.Component {
constructor(props) {
super(props)
this.el = document.createElement('div')
}
componentDidMount() {
document.getElementById('modal').appendChild(this.el)
}
componentWillUnmount() {
document.getElementById('modal').removeChild(this.el)
}
render() {
return createPortal(
this.props.children,
this.el
)
}
}
We can have an App component render it, and all the events happening in the Modal component will be handled by App even though technically the modal is rendered in a different DOM tree. Mount the app with createRoot from react-dom/client (same mount API as in the React Components post):
import { createRoot } from 'react-dom/client'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {showModal: false}
this.handleShow = this.handleShow.bind(this)
this.handleHide = this.handleHide.bind(this)
}
handleShow() {
this.setState({showModal: true})
}
handleHide() {
this.setState({showModal: false})
}
render() {
const modal = this.state.showModal ? (
<Modal>
<div>
The modal <button onClick={this.handleHide}>Hide</button>
</div>
</Modal>
) : ''
return (
<div>
The app <button onClick={this.handleShow}>Show modal</button>
{modal}
</div>
)
}
}
createRoot(document.getElementById('app')).render(<App />)
See the full example on https://codepen.io/flaviocopes/pen/KbdagX
Want me to talk about your product? You can sponsor this site.