React Portals are a powerful feature in React that allows you to render components outside the current React tree hierarchy. With portals, you can easily render elements into different parts of the DOM, such as modals, tooltips, or any other component that needs to break out of the component’s container.
In this article, we’ll explore React Portals, how they work, and how you can use them effectively in your React applications.
What are React Portals?
React Portals provide a way to render components into a DOM node outside the component hierarchy. Typically, when you render a component in React, it gets inserted into the DOM as a child of its parent component. However, there are scenarios where you might need to render a component at a different location in the DOM, such as when creating a modal or dropdown menu.
React Portals solve this problem by allowing you to render a component’s output to a different DOM node outside of its parent component’s DOM hierarchy. This enables you to create components that can be visually displayed at any location in the DOM while still maintaining their logical position in the component hierarchy.
Under the hood, React Portals use a feature called “portal” in react-dom
. react-dom
provides a method called createPortal
that takes two arguments: the first is the content you want to render, which is also referred to as children, and the second is the DOM node where you want to render it, as shown below:
import {createPortal} from 'react-dom';
createPortal(children, domNode);
Some use cases of React Portals include:
- Modal dialogs: modals typically overlay the entire page and require rendering outside the current component hierarchy to ensure that they appear on top of other content. React Portals allow you to easily render modal components outside of their parent components and manage their visibility and behavior.
- Tooltips and popovers: tooltip and popover components often need to be rendered close to a specific element or position on the page. React Portals enable you to render these components in a different part of the DOM, precisely positioned relative to the triggering element.
- Dropdown menus: dropdown menus require rendering a menu component outside the parent component to ensure that it appears correctly in terms of positioning and layering. React Portals make it convenient to render dropdown menus outside of the regular component hierarchy, while still maintaining their interaction with the triggering elements.
Benefits of using React Portals
React Portals offer several benefits when it comes to building React applications, some of which are:
- Flexible Component Placement: React Portals allow you to render components at any location in the DOM, even outside of the current component hierarchy. This provides greater flexibility in designing complex UIs and positioning components precisely where needed.
- Separation of Concerns: portals enable you to separate the visual representation of a component from its logical position in the component hierarchy. This separation improves code organization and makes understanding and maintaining your codebase easier.
- Avoidance of CSS Conflicts: by rendering components via portals, you can isolate their styles from their parent and sibling components. This reduces the likelihood of CSS conflicts and ensures that the styles of your portal components remain unaffected by the surrounding environment.
- Enhanced Accessibility: portals are especially useful for creating accessible components like modals. By rendering them at the top level of the DOM, screen readers and assistive technologies can access the content more easily, improving the overall accessibility of your application.
- Integration with Third-Party Libraries: React Portals facilitate the integration of third-party libraries or components that require rendering outside of the main React hierarchy. This enables the seamless incorporation of external functionalities while preserving the integrity of your React application.
- Optimized Performance: React Portals can help improve the performance of your application by minimizing unnecessary re-renders. Since portal components exist outside of the regular component hierarchy, they can be updated independently, preventing unnecessary updates to the entire application.
How to use React Portals
To demonstrate how react portals work, letβs create a modal example.
Installing React
Since create-react-app no longer recommended, I’ll be installing React using Vite. Create your project and select React as the framework:
$ npm create vite@latest
npx: installed 1 in 0.969s
β Project name: β¦ my-project-name
β Select a framework: βΊ React
β Select a variant: βΊ JavaScript
You can, of course, use create-react-app
instead if you prefer.
App component
Weβll create an app component to render a button element and a modal component. The modal will only be visible when we click on the button.
import React, { useState } from 'react'
import './App.css';
import Modal from './Modal.jsx';
export default function App() {
const [isOpen, setOpen]= useState(false)
return (
<div>
<button onClick={()=> {setOpen(true)}}>show button</button>
<Modal isOpen= {isOpen} onClose={()=>setOpen(false)}/>
</div>
)
};
The code above sets up an App component with a button that toggles the display of a modal component based on the state of the isOpen
variable. Clicking the button shows the modal, and triggering the onClose
function closes the modal by updating the state.
Modal component
Weβll use createPortal
inside of our modal component. Letβs create a file called Modal.jsx
and paste this in:
import React from 'react'
import {createPortal} from 'react-dom';
import './Modal.css';
export default function Modal({isOpen, onClose}) {
if(!isOpen)return null;
return createPortal(
<div className='modal'>
<div className='modal-container'>
<div className='modal-body'>
<p>sample modal</p>
</div>
<button onClick={onClose}>Close</button>
</div>
</div>
, document.getElementById('modal') // this will let react-dom know that we want to render this modal outside the current React tree
)
}
The modal component receives two props: isOpen
and onClose
. The isOpen
prop indicates whether the modal should be visible or hidden, while the onClose
prop is a function to be called when the modal is closed.
If the isOpen
prop is false
, then the modal will not be displayed, and the component returns null, effectively rendering nothing.
When the isOpen
prop is true
, it executes the createPortal function. This function renders the modal content outside the current React component tree. It takes two parameters: the JSX code representing the modal component and the target DOM element where the modal should be rendered. In our case, the target DOM element is specified as document.getElementById('modal')
. This instructs react-dom
to render the modal in the DOM element with the modal
id, effectively placing it outside the regular React component hierarchy.
Creating our modal container
For that, head over to the index.html file. There, youβll see the root
div element. To explain things, this means that whatever we are rendered in React is being rendered within the root div container.
When you review the main.jsx
file (for vite) or index.js
file (for create-react-app), you will notice that react-dom
uses the specified ID as the root element. It creates a new structure within the DOM specifically for React components and proceeds to render each component within this structure.
Going back to building our project, inside our index.html
, create another id called modal:
<div id="modal"></div>
Adding some styling
Let’s add some basic styling to our modal:
p{
color: black;
}
.modal{
position: fixed;
z-index: 2;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
overflow: auto;
}
.modal-container{
background-color: #fefffe;
margin: 10% auto;
width: 50%;
padding: 10px;
}
Now, let’s start the server to see the components in action. Run the following command and open the URL http://127.0.0.1:5173/ in your browser:
$ npm run dev
Our Result:
Let’s examine the developer tools and inspect the elements. We can see that the button element is rendered within the main root
container, which represents the main container where the entire React application is rendered. However, the modal component, although defined within the parent app component, is rendered in a separate target location in the DOM, typically outside of the regular React tree structure.
In our code, you may notice that the modal component is a child component of a div
element, which is a part of the app component. The app component is centrally positioned within the root
container. React Portals allow us to define the modal component within the parent app component, but the actual rendering of the modal occurs in a different part of the DOM, outside the regular React tree structure. This feature enables us to pass values to the modal’s props and render content within the modal component.
Caveats and Considerations
While React Portals provide powerful capabilities, there are some caveats and considerations to keep in mind:
- Usage of external DOM nodes: when using portals, you must ensure that the target DOM node exists in the document. You might encounter errors or unexpected behavior if the target node is absent. It’s a good practice to check if the target node exists before rendering the portal content.
- Event bubbling: by default, events emitted from a portal component will propagate up the React tree and trigger event handlers on ancestor components. This behavior might not always be desired. StopPropagation() or other event-handling techniques can prevent event bubbling and ensure that events are handled only within the portal component.
- CSS considerations: since portal components are rendered outside the normal React component hierarchy, you need to be cautious when styling them. CSS rules that rely on parent-child relationships might not work as expected. It’s recommended to use explicit CSS selectors or scoped styles to ensure proper styling of portal components.
- Performance implications: React Portals improve performances in some cases but they can also limit performance in some ways, depending on how you use them. Let’s consider an example to illustrate the performance impact of portals. Imagine you have a React component responsible for rendering a list of 1,000 items. If you render this list without employing a portal, React will only need to perform one render pass. However, if you opt to use a portal to render the list, React will need to perform two render passes. The initial pass will render the list to a virtual DOM tree, followed by the second pass rendering the virtual DOM tree to the actual DOM.
As the number of render passes increases, the impact on performance becomes more significant, particularly when working with extensive content rendered via portals. If you have concerns about performance, it is advisable to use portals sparingly and only when they are absolutely necessary.
Conclusion
React Portals provide a powerful mechanism for rendering components outside their parent component hierarchy. They allow you to create visually complex UIs with components that can be rendered at any location in the DOM. Using portals allows you to separate concerns, improve accessibility, and achieve greater flexibility in building your React applications.
It’s important to consider the caveats and best practices mentioned in this guide to ensure proper usage and avoid potential issues when using portals. With careful implementation, React Portals can greatly enhance the capabilities of your React applications and provide a better user experience.
So go ahead and leverage the power of React Portals to create dynamic, interactive, and visually appealing components in your React projects.