React Portals provide a way to render components outside the main DOM hierarchy, which is particularly useful for elements that need to overlay other elements, like modals, tooltips or dropdowns.
While React generally keeps components nested within the hierarchy of their parent components, Portals allow us to bypass this hierarchy to improve control over styling and event handling.
What are React Portals?
React Portals are a feature introduced in React 16 that allow you to render components outside of the root DOM node.
By creating a portal, you can place elements, like modals or notifications, in a different location in the DOM tree, while still keeping them a part of your React component hierarchy. This means they will still maintain access to the state and props of the React component that created them.
Why Use Portals?
Using Portals provides several key benefits:
- Improved Layering: Portals are often used for UI elements that need to overlay other elements, such as modals, popups and tooltips. By rendering these components outside the regular DOM tree, Portals ensure that these elements appear above other content.
- Avoiding CSS Conflicts: Sometimes, CSS positioning rules (like position: relative or overflow: hidden) on parent components can restrict child components. With Portals, these elements can be separated from their parent styles, ensuring they are not clipped or hidden by parent CSS properties.
- Better Event Management: Even though Portals are rendered outside the DOM hierarchy, they maintain event bubbling as if they were a normal child component. This means that events originating in a Portal component will still propagate to their React parents, making event management simpler.
Creating a Portal
To create a Portal, you use the ReactDOM.createPortal method. This method takes two arguments:
- React Node: The JSX of the component you want to render in the Portal.
- DOM Node: The target DOM node where the Portal content should be rendered.
Here’s the syntax:
ReactDOM.createPortal(child, container)
- child is the JSX element to render.
- container is the DOM node where you want the element to be rendered.
Example: Creating a Modal with React Portals
Suppose you want to create a modal dialog box using Portals. Here’s how you would do it:
Set up the HTML Structure: First, you need to create a DOM node outside your root element in index.html. This will be the target for your Portal:
<div id="root"></div>
<div id="modal-root"></div>
Create the Modal Component: Here, we create a Modal component that uses ReactDOM.createPortal to render its content in the modal-root div.
import React from 'react';
import ReactDOM from 'react-dom';
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null; // Return nothing if modal is closed
return ReactDOM.createPortal(
<div style={modalStyles}>
<div style={contentStyles}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
document.getElementById('modal-root')
);
}
const modalStyles = {
position: 'fixed',
top: '0',
left: '0',
right: '0',
bottom: '0',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
const contentStyles = {
backgroundColor: 'white',
padding: '20px',
borderRadius: '5px',
width: '300px',
textAlign: 'center',
};
export default Modal;
- We check if the modal is open; if not, we return null so nothing renders.
- We use ReactDOM.createPortal to render the modal contents within the modal-root node.
- The modal has a backdrop style (modalStyles) and the content style (contentStyles), creating a typical modal overlay.
Using the Modal Component: Now, we’ll create a parent component that uses the Model component.
import React, { useState } from 'react';
import Modal from './Modal';
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
return (
<div>
<h1>React Portals Example</h1>
<button onClick={openModal}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={closeModal}>
<h2>Modal Content</h2>
<p>This is a modal rendered through a Portal.</p>
</Modal>
</div>
);
}
export default App;
In this example:
- When the user clicks the “Open Modal” button, isModalOpen is set to true, triggering the Modal to display.
- Clicking “Close” within the modal triggers setIsModalOpen(false), which closes the modal by rendering null.
Benefits of Using Portals for Modals
By placing the modal in a Portal, it will:
- Render above other content, regardless of the nesting structure in the main DOM.
- Avoid being constrained by the CSS styles of the parent components in the normal DOM hierarchy.
- Ensure that events from the modal will still propagate back to the main React component tree as expected.
Real-World Use Cases for Portals
- Tooltips and Dropdowns: Portals are ideal for placing tooltips and dropdowns that need to overlay other content. For example, a dropdown within a nested component can escape potential clipping issues by rendering outside the main DOM hierarchy.
- Notifications: Notifications often appear at the top level of an application. Portals allow you to display these notifications without affecting the main DOM layout.
- Overlays and Popups: Any overlay or popup that should not be affected by its parent component’s layout or styles benefits from Portals.
Event Bubbling in Portals
One of the remarkable aspects of React Portals is that they do not break the event bubbling mechanism. Events from elements rendered in a Portal still propagate to their parents in the React component tree, as if they were a normal child. This feature is particularly useful for managing click events on modals or popups that require interaction with their parent components.