What is Portal?
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
Portals let your components render some of their children into a different place in the DOM.
What happens while rendering any react app?
While rendering any React application, a single DOM element is used to render the whole React tree.export
const PlayerScoreList = ({ scores }) => { return (As you can see we are rendering our react component into a DOM element having an id root.
Why do we need a Portal?
A portal only changes the physical placement of the DOM node. In every other way, the JSX you render into a portal acts as a child node of the React component that renders it.
A typical use case for portals is when a parent component has an
overflow: hidden
orz-index
style, but you need the child to visually “break out” of its container. For example, dialogs, hovercards, and tooltips.
The child can access the context provided by the parent tree, and events still bubble up from children to parents according to the React tree.
How can we do this?
This is where ReactDOM.createPortal() comes into play. To create a portal, call createPortal, passing some JSX, and the DOM node where it should be rendered.
index.html
<html>
<body>
<div id="root"></div>
<div id="another-root"></div>
</body>
</html>
App.js
import ReactDOM from 'react-dom';
import React from 'react';
const mainContainer = document.querySelector('#root');
const portalContainer = document.querySelector('#another-root'
const HelloPortal = () => {
return (
<div>Hi, This is from a React Portal.</div>
);
}
const HelloReact = () => {
return (
<div>
<h1>Hello React App</h1>
{ReactDOM.createPortal(<HelloPortal />, portalContainer)}
</div>
);
}
ReactDOM.render(<HelloReact />, mainContainer);
You can use devtools inspect element and see that <h1>Hello React App</h1> is rendered inside #another-root tag, whereas <h1>Hi, This is from a React Portal.</h1> is rendered inside #root tag.
Usage of createPortal
Rendering to a different part of the DOM
This lets a part of your component “escape” from whatever containers it may be in. For example, a component can display a modal dialog or a tooltip that appears above and outside of the rest of the page.
import ReactDOM from 'react-dom';
import React from 'react';
const mainContainer = document.querySelector('#root');
const HelloReact = () => {
return (
<div style={{ border: '2px solid black' }}>
<p>This is placed inside the parent div.</p>
{ReactDOM.createPortal(
<p>
This will be placed inside <b> document.body </b> using portals.
</p>,
portalContainer,
)}
</div>
);
}
ReactDOM.render(<HelloReact />, mainContainer);
Without a portal, the second <p> would be placed inside the parent <div>, but the portal “teleported” it into the document.body./
Rendering a modal dialog with a portalt
Portal can be used to create a modal that floats above the page, even if the component that invokes the dialog is inside a container with overflow: hidden or other styles that interfere with the dialog like z-index, etc.
const Modal = (props) => {
const {onClose} = props;
return (
<div className='modal'>
<div>Hi! I am a modal.</div>
<button onClick={onClose}>Close</button>
</div>
)
}
const PortalDemo = () => {
const [showModal, setShowModal] = useState(false);
const handleCloseModal = () => setShowModal(false);
const handleOpenModal = () => setShowModal(true);
const portalNode = document.querySelector('#another-root');
return (
<button onClick={handleOpenModal}> Show Modal using Portal </button>
{showModal && ReactDOM.createPortal(
<Modal onClose={handleCloseModal} />,
portalNode,
)}
);
}
const NoPortalDemo = () => {
const [showModal, setShowModal] = useState(false);
const handleCloseModal = () => setShowModal(false);
const handleOpenModal = () => setShowModal(true);
const portalNode = document.querySelector('#another-root');
return (
<button onClick={handleOpenModal}> Show Modal using Portal </button>
{showModal && ReactDOM.createPortal(
<Modal onClose={handleCloseModal} />,
portalNode,
)}
);
}
const App = () => {
return (
<>
<div style={{ border: '2px solid black' }}>
<NoPortalDemo />
</div>
<div style={{ border: '2px solid black' }}>
<PortalDemo />
</div>
</>
);
}
Rendering react components into non-React DOM nodes
Portals can be useful if the React root is only part of a static or server-rendered page that isn’t built with React. For example, if your page is built with a server framework like Rails or PHP, you can create areas of interactivity within static areas such as sidebars.
index.html
<html>
<head><title>My app</title></head>
<body>
<h1>Welcome to this hybrid app</h1>
<div class="parent">
<div class="sidebar">
This is server non-React markup
<div id="sidebar-content"></div>
</div>
<div id="root"></div>
</div>
</body>
</html>
App.js
import React from 'react';
import ReactDOM from 'react-dom';
const sidebarContentEl = document.getElementById('sidebar-content');
const App = () => {
return (
<>
<MainContent />
{ReactDOM.createPortal(
<SidebarContent />,
sidebarContentElement,
)}
</>
);
}
const root = React.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);