Understanding ReactDOM createPortal: A Comprehensive Guide with Examples

Love Trivedi
4 min readSep 13, 2024

In React, components are typically rendered inside the DOM tree of their parent component. However, there are scenarios where you might want to render a component outside this parent-child structure — for example, when creating modals, tooltips, or dropdowns that need to “break out” of the normal DOM hierarchy. This is where ReactDOM.createPortal comes into play.

What is createPortal?

createPortal is a method provided by React that allows you to render a component (or any React node) into a different part of the DOM tree, away from its parent hierarchy.

Syntax:

ReactDOM.createPortal(child, container);
  • child: The JSX or React component you want to render.
  • container: The DOM node where you want to append the child.

When to Use createPortal?

  • Modals: Modals usually need to break out of the normal flow to ensure they are not constrained by parent component styles.
  • Tooltips: Tooltips must often be rendered outside their parent to avoid clipping by parent boundaries.
  • Dropdowns: A dropdown might overflow or behave unexpectedly if constrained by the parent’s overflow or positioning.

Example 1: Creating a Simple Modal with createPortal

Let’s start by building a basic modal component using createPortal. The modal should be rendered into a div with the ID modal-root outside the main app structure.

Setup

First, ensure your HTML file has a designated modal-root element.

<body>
<div id="root"></div>
<div id="modal-root"></div> <!-- This is where our modals will be rendered -->
</body>

Modal Component

import React from 'react';
import ReactDOM from 'react-dom';

function Modal({ children, onClose }) {
// Create a portal to render modal children into `modal-root`
return ReactDOM.createPortal(
<div className="modal-overlay">
<div className="modal">
<button onClick={onClose}>Close Modal</button>
{children}
</div>
</div>,
document.getElementById('modal-root')
);
}

App 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>Welcome to the Portal Demo</h1>
<button onClick={openModal}>Open Modal</button>

{isModalOpen && (
<Modal onClose={closeModal}>
<h2>This is a portal modal!</h2>
</Modal>
)}
</div>
);
}

export default App;

Styling the Modal

.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
}

.modal {
background-color: white;
padding: 20px;
border-radius: 8px;
width: 300px;
text-align: center;
}

With this setup, when you click the “Open Modal” button, the modal component will render outside the root of the app, inside modal-root.

Example 2: Rendering a Tooltip Using createPortal

Tooltips often need to be positioned relative to an element and rendered outside the normal flow of the parent element, making createPortal an excellent solution.

Tooltip Component

import React from 'react';
import ReactDOM from 'react-dom';

function Tooltip({ message, target }) {
const tooltipStyles = {
position: 'absolute',
top: target.top + window.scrollY + target.height,
left: target.left + window.scrollX,
backgroundColor: 'black',
color: 'white',
padding: '5px',
borderRadius: '4px',
};

return ReactDOM.createPortal(
<div style={tooltipStyles}>
{message}
</div>,
document.body // Rendering tooltip in the document body
);
}

App Component with Tooltip

import React, { useState, useRef } from 'react';
import Tooltip from './Tooltip';

function App() {
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
const [tooltipPosition, setTooltipPosition] = useState({});
const buttonRef = useRef();

const showTooltip = () => {
const rect = buttonRef.current.getBoundingClientRect();
setTooltipPosition(rect);
setIsTooltipVisible(true);
};

const hideTooltip = () => {
setIsTooltipVisible(false);
};

return (
<div>
<h1>Hover over the button to see the tooltip</h1>
<button
ref={buttonRef}
onMouseEnter={showTooltip}
onMouseLeave={hideTooltip}
>
Hover me
</button>

{isTooltipVisible && (
<Tooltip message="This is a tooltip" target={tooltipPosition} />
)}
</div>
);
}

export default App;

In this example, when you hover over the button, the tooltip will appear in a floating box outside the normal component tree.

Example 3: Managing Dropdowns with createPortal

Similar to tooltips, dropdowns may require rendering outside the regular DOM tree to prevent layout issues.

Dropdown Component

import React from 'react';
import ReactDOM from 'react-dom';

function Dropdown({ options, target }) {
const dropdownStyles = {
position: 'absolute',
top: target.top + window.scrollY + target.height,
left: target.left + window.scrollX,
backgroundColor: 'white',
border: '1px solid black',
padding: '10px',
borderRadius: '4px',
zIndex: 1000
};

return ReactDOM.createPortal(
<ul style={dropdownStyles}>
{options.map(option => <li key={option}>{option}</li>)}
</ul>,
document.body // Rendering dropdown in the document body
);
}

Using the Dropdown in App

import React, { useState, useRef } from 'react';
import Dropdown from './Dropdown';

function App() {
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
const [dropdownPosition, setDropdownPosition] = useState({});
const buttonRef = useRef();

const toggleDropdown = () => {
const rect = buttonRef.current.getBoundingClientRect();
setDropdownPosition(rect);
setIsDropdownVisible(!isDropdownVisible);
};

return (
<div>
<h1>Click the button to see the dropdown</h1>
<button ref={buttonRef} onClick={toggleDropdown}>
Toggle Dropdown
</button>

{isDropdownVisible && (
<Dropdown options={['Option 1', 'Option 2', 'Option 3']} target={dropdownPosition} />
)}
</div>
);
}

export default App;

Benefits of createPortal

  • Escaping Parent Overflow: By rendering elements outside the regular DOM tree, createPortal helps you avoid layout constraints imposed by parent elements (like overflow: hidden).
  • Global Elements: For UI components like modals or notifications that require a global presence, createPortal allows them to live outside the main app hierarchy while maintaining React's reconciliation.

Conclusion

ReactDOM.createPortal is a powerful tool in React for rendering elements outside the typical DOM hierarchy. Whether you're building modals, tooltips, or dropdowns, createPortal enables flexible positioning and styling of components without being constrained by parent elements.

By mastering portals, you’ll be able to create more dynamic, user-friendly, and visually appealing React applications that break free of traditional component hierarchies.

--

--

Love Trivedi

Full Stack Developer | Problem Solver | Knowledge Share, 🚀 Expertise: JavaScript enthusiast specializing in ReactJS, Angular, and Node.js.