Clean β’ Professional
React Portals are a powerful feature that allow you to render components outside the main DOM hierarchy of your React app. This is especially useful for creating modals, tooltips, dropdowns, or overlays, which need to appear above other content without breaking the layout.
Using portals ensures your UI remains clean, manageable, and accessible, while still keeping Reactβs state and event system intact.
In a normal React application, all components render inside a single root DOM element:
<div id="root"></div>
Sometimes, certain components need to render outside this root to avoid layout conflicts, z-index issues, or overflow restrictions. Examples include:
React provides the ReactDOM.createPortal() method for this purpose.
Syntax of a Portal
ReactDOM.createPortal(child, container)
This allows React components to maintain their parent-child relationships and state, even if rendered elsewhere in the DOM.
Example: Basic Portal
HTML:
<div id="root"></div>
<div id="modal-root"></div>
React Component:
import React from "react";
import ReactDOM from "react-dom";
function Modal({ children }) {
return ReactDOM.createPortal(
<div className="modal">{children}</div>,
document.getElementById("modal-root")
);
}
function App() {
return (
<div>
<h1>Welcome to React Portals</h1>
<Modal>
<h2>This content is rendered outside the main DOM root!</h2>
</Modal>
</div>
);
}
export default App;
<Modal> content is rendered inside the modal-root div, separate from the main app root.Portals are particularly useful for modals because they often need to overlay the entire screen. Hereβs a more practical example:
import { createRoot } from 'react-dom/client';
import { useState } from 'react';
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return createPortal(
<div style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<div style={{
background: 'white',
padding: '20px',
borderRadius: '8px'
}}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
document.body
);
}
function MyApp() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<h1>My App</h1>
<button onClick={() => setIsOpen(true)}>Open Modal</button>
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
<h2>Modal Content</h2>
<p>This content is rendered outside the App component!</p>
</Modal>
</div>
);
}
createRoot(document.getElementById('root')).render(<MyApp />);
createPortal renders the modal content into document.body, outside the main App hierarchy.useState hook.Even though a portal renders content outside the parent DOM, events still bubble up through the React component tree as if the portal were a normal child.
Example:
import { createRoot } from 'react-dom/client';
import { useState } from 'react';
import { createPortal } from 'react-dom';
function PortalButton({ onClick, children }) {
return createPortal(
<buttononClick={onClick}
style={{
position: 'fixed',
bottom: '20px',
right: '20px',
padding: '10px',
background: 'blue',
color: 'white'
}}>
{children}
</button>,
document.body
);
}
function App() {
const [divClicks, setDivClicks] = useState(0);
const [buttonClicks, setButtonClicks] = useState(0);
return (
<divstyle={{ padding: '20px', border: '2px solid black', margin: '20px' }}
onClick={() => setDivClicks(c => c + 1)}
>
<h2>Div Clicked: {divClicks}</h2>
<h2>Button Clicked: {buttonClicks}</h2>
<PortalButton onClick={() => setButtonClicks(c => c + 1)}>
Floating Button
</PortalButton>
</div>
);
}
createRoot(document.getElementById('root')).render(<App />);
overflow: hidden, z-index conflicts, or complex CSS layouts.