React Portals: A Practical Guide for Beginners
React Portals are a highly effective feature in React that enable you to render components outside the usual React hierarchy. Notably, this functionality maintains the parent-child relationship and the event bubbling mechanism intact.
This can be useful for scenarios where you need to display modals, tooltips, dropdowns, or other UI elements that need to break out of their parent container’s overflow or z-index constraints.
What are React Portals?
React Portals are a way of creating a new DOM node and rendering a component into it, instead of rendering it as a child of the nearest parent node in the DOM tree. This means that the component can be rendered anywhere in the DOM, even outside of the root element where your React app is mounted.
However, this does not mean that the component is detached from the React hierarchy. The component still behaves like a normal React child in every other way, such as receiving props, context, and lifecycle methods from its parent component. It also still participates in the event bubbling mechanism, meaning that any event fired from inside the portal will propagate to its ancestors in the React tree, even if they are not ancestors in the DOM tree.
How do React Portals work?
To create a portal, you use the ReactDOM.createPortal function. It requires two things: a child component and a container element. The child component is any renderable React element, such as an element, string, fragment, or another component. The container element is a DOM node where you want to render the child component.
For example, let’s say you have the following HTML structure:
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
And you have a Modal component that you want to render into the modal-root element, instead of the app-root element where your React app is mounted. You can use ReactDOM.createPortal like this:
import ReactDOM from "react-dom";
function Modal(props) {
// Create a new div element
const el = document.createElement("div");
// Append it to the modal-root element
const modalRoot = document.getElementById("modal-root");
modalRoot.appendChild(el);
// Use ReactDOM.createPortal to render the props.children into the new div element
return ReactDOM.createPortal(props.children, el);
}
export default Modal;
Now you can use the Modal component anywhere in your React app, and it will be rendered into the modal-root element instead of its parent node.
How to use React Portals in an Example?
To demonstrate how to use React Portals in a real-world scenario, let’s build a simple app that shows a list of products and allows the user to click on each product to see more details in a modal window.
First, let’s create a Product component that renders a product name and an image:
import React from "react";
function Product(props) {
const { name, image } = props;
return (
<div className="product">
<h3>{name}</h3>
<img src={image} alt={name} />
</div>
);
}
export default Product;
Next, let’s create a ProductList component that renders an array of products using the Product component:
import Product from "./Product";
function ProductList(props) {
const { products } = props;
return (
<div className="product-list">
{products.map((product) => (
<Product key={product.id} {...product} />
))}
</div>
);
}
export default ProductList;
Then, let’s create a ProductDetails component that renders more information about a product, such as its description and price:
import React from "react";
function ProductDetails(props) {
const { name, image, description, price } = props;
return (
<div className="product-details">
<h3>{name}</h3>
<img src={image} alt={name} />
<p>{description}</p>
<p>${price}</p>
</div>
);
}
export default ProductDetails;
Finally, let’s create an App
component that renders the ProductList
component and uses the Modal
component to show the ProductDetails
component when a product is clicked. We also need to keep track of the selected product and the modal visibility in the state:
import React, { useState } from "react";
import ProductList from "./ProductList";
import Modal from "./Modal";
import ProductDetails from "./ProductDetails";
// Some mock data for products
const products = [
{
id: 1,
name: "iPhone 14",
image: "https://example.com/iphone14.jpg",
description: "The latest and greatest smartphone from Apple.",
price: 999,
},
{
id: 2,
name: "MacBook Pro",
image: "https://example.com/macbookpro.jpg",
description: "The most powerful laptop for professionals.",
price: 1999,
},
{
id: 3,
name: "AirPods Pro",
image: "https://example.com/airpodspro.jpg",
description: "The best wireless earbuds with noise cancellation.",
price: 249,
},
];
function App() {
// The state for the selected product
const [selectedProduct, setSelectedProduct] = useState(null);
// The state for the modal visibility
const [isModalOpen, setIsModalOpen] = useState(false);
// The handler for clicking on a product
const handleProductClick = (product) => {
// Set the selected product to the clicked product
setSelectedProduct(product);
// Open the modal
setIsModalOpen(true);
};
// The handler for closing the modal
const handleModalClose = () => {
// Set the selected product to null
setSelectedProduct(null);
// Close the modal
setIsModalOpen(false);
};
return (
<div className="app">
<h1>React Portals Example</h1>
{/* Render the product list and pass the click handler as a prop */}
<ProductList products={products} onProductClick={handleProductClick} />
{/* Render the modal if it is open and pass the close handler as a prop */}
{isModalOpen && (
<Modal onClose={handleModalClose}>
{/* Render the product details inside the modal using the selected product */}
<ProductDetails {...selectedProduct} />
</Modal>
)}
</div>
);
}
export default App;
And that’s it! We have successfully used React Portals to render a modal window outside of the normal React hierarchy, while still maintaining the parent-child relationship and the event bubbling mechanism.
Conclusion
In this blog, we covered the basics of React Portals – what they are, how they function, and provided a straightforward example to help you understand their practical usage. We saw how React Portals can help us render components outside of their parent container’s overflow or z-index constraints, such as modals, tooltips, dropdowns, or other UI elements that need to break out of their normal position.
React Portals are a powerful feature that can make your UI more flexible and dynamic. However, they also come with some caveats and challenges, such as managing keyboard focus, accessibility, and performance. Therefore, you should use them wisely and only when necessary.