Normally in React when a component is rendered, it is mounted into the DOM as a child of the nearest parent node. This
works fine for most of the scenarios but sometimes you may want to insert a child into a different location in the DOM.
Portals
in react provide a way to render children into a DOM node that exists outside the DOM hierarchy of
the parent component.
Why React portal
So, why is it important to render children into a DOM node outside the parent hierarchy sometimes? There are couple of advantages which are visible when you are using a modal, hovercards or tooltips, basically something which should be displayed as an overlay not as part of the parent component.
- First advantage is to have a semantically correct HTML. Logically a modal or hovercard should not be part of “root” element, it should be rendered in a separate DOM node.
- Poroperties for the child node are inherited from the parent component. For example, height, width, overflow: hidden, z-index which may cause modal, tooltip or hover card to be cropped or some part of it hidden.
To avoid the above-mentioned issues, you can use portals in React to render children into a separate DOM node.
React portal Syntax
A portal is created using createPortal()
method which is part of react-dom package.
ReactDOM.createPortal(child, container)
Parameters
child- Something that can be rendered such as an element, string, or fragment.
container- DOM node location where child should be injected.
React portal example
As an example, we'll create a Modal dialog box where we need to have a backdrop and the modal box itself. If you click anywhere on the backdrop, modal box should close. There will also be a close button with in the box.
Initially we won't use React portal to render this Modal box which means it will be rendered with in the Parent component.
ModalDialog.js
import './ModalDialog.css' const ModalDialog = (props) => { return ( <div className="modal-backdrop" onClick={props.handleClose}> <div className="modal-content"> {props.children} <button onClick={props.handleClose}>Close</button> </div> </div> ); } export default ModalDialog;
ModalDialog.css
.modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100vh; z-index: 20; background-color: rgba(0, 0, 0, 0.75); } .modal-content { top: 12vh; left: 5%; width: 50%; height: 40%; background-color: white; padding: 1rem; border-radius: 10px; z-index: 30; display: flex; align-items: center; justify-content: center; font-size: 1.4rem; }
App.js
Here we have state management for modal (open or closed) using useState hook. There is also a button to open Modal box, clicking this button sets the isModalOpen to true which results in modal box being displayed.
Closing the modal triggers the closeHandler() function which is kind of child to parent component communication by lifting the state up.
import ModalDialog from "./Components/UI/ModalDialog"; function App() { const [isModalOpen, setIsModalOpen] = useState(false); const closeHandler = () => { setIsModalOpen(false); } return ( <div> <button onClick={() => setIsModalOpen(true)}> Click to Open Modal </button> {isModalOpen && <ModalDialog handleClose={closeHandler}> This is Modal Content </ModalDialog>} </div> ); };
As you can see Modal is rendered as a Child component of App component wrapped with in root element.
Let's make changes to render Modal outside the DOM hierarchy of the parent component.
In public\index.html add another <div> element. This <div> element is the container for injecting the Modal.
<div id="modalOverlay"></div> <div id="root"></div>
In the ModalDialog.js wrap the code with in createPortal() method.
import reactDom from 'react-dom'; import './ModalDialog.css' const Modal = (props) => { return ( <div className="modal-backdrop" onClick={props.handleClose}> <div className="modal-content"> {props.children} <button onClick={props.handleClose}>Close</button> </div> </div> ); } const portalElement = document.getElementById('modalOverlay'); const ModalDialog = (props) => { return reactDom.createPortal(<Modal handleClose={props.handleClose}>{props.children}</Modal>, portalElement); } export default ModalDialog;
Points to note here-
- First you need to import from react-dom. You can also import createPortal directly
import { createPortal } from 'react-dom';
- createPortal() method takes two arguments, first is the child that has to be rendered. To make it more readable what constitutes a child has been assigned to a constant Modal.
- Second argument to the createPortal() method is a container. We already added one <div> element in the index.html with id as “modalOverlay”. That element is assigned to another constant portalElement.
- Use of portal only changes the DOM tree structure not the parent child component relationship. <ModalDialog> is still used in App.js to render the child component. props are also used to have parent-child communication the same way as before.
Now Modal component is rendered into a separate DOM node outside the DOM hierarchy of the parent component.
That's all for the topic React Portals With Examples. If something is missing or you have something to share about the topic please write a comment.
You may also like
- React Fragments With Examples
- React Context API With Examples
- React.memo With Examples
- React useReducer Hook With Examples
- React Form + Formik + Yup Validation Example
- Java HashMap computeIfPresent() With Examples
- Stable or Unstable Number Java Program
- What is In-place Algorithm
- Setter Dependency Injection in Spring
- Spring @Conditional Annotation