React createPortal is a function that lets you render some children into a different part of the DOM
<div>
<TestComponent />
{createPortal(children, domNode, key?)}
</div>
This can be useful when you want to render some components on top of another component
Some examples include dropdown menus, tooltips etc
If you are looking for a React Native Chat SDK to build in app chat messaging application, you can consider DeadSimpleChat React Native SDK
How does createPortal work?
you can create a portal by calling the createPortal
function an
A portal changes the physical placement of the DOM node. But in all other ways the JSX you render through the portal acts as a child of the component that is rendering it
As an example:
the events will bubble up from the child to the parent and the child can also access the context provided by the parent.
import { createPortal } from 'react-dom';
// ...
<div>
<p>This child inside div.</p>
{createPortal(
<p>This child is inside body.</p>,
document.body
)}
</div>
Parameters
Children
: a <div /> tag or string or an array or some JSX basically anything that can be rendered with react can be children and passed to the createPortal function as a propdomNode
: A DOM node, such as those returned bydocument.getElementById()
The node must already exist. Portal content will be recreated if a DOM node is passed during an updateoptional
Key: A unique key or a string that is used as the portals key
Returns
createPortal returns a react node that can be included in to JSX or returned from a React component
Caveats
- Events from the portal propogate according to the react tree and not according to the DOM tree.
- This could cause issues like If you have an onClick on a div the onClick event handler function will fire. If this causes issues either stop the event propagation from inside the portal or move the portel up the React tree
Let us consider some examples of the React portal
Dead Simple Chat allows you to easily add Chat to any React Application using powerful Javascript Chat SDK.
Example 1: Rendering a p tag from inside a div to body tag using Portal
Portal lets you place a DOM node and its children in a different part of the DOM. It creates a Portal through which the element can travel to a different part.
Let us take an example where a modal dialog might appear on top of the rest of the page
To create a portal call the create portal function with some JSX that you want to render and the DOM node where you want to render it
ReactDOM.createPortal(JSX that you want to render, DOM node);
React will put the DOM nodes for the JSX that you provided inside the DOM node you provided
import { createPortal } from "react-dom";
function TestComponent() {
return (
<div style={{ border: "2px solid black" }}>
<p>This child is inside the above div.</p>
{createPortal(
<p>This child is inside document body.</p>,
document.body
)}
</div>
);
}
Without the portal the second p tag will be inside the div but the portal transported it to be inside the body tag
import { createPortal } from "react-dom";
export default function TestComponent() {
return (
<div style={{ border: "5px solid red" }}>
<p>This child is inside the above div.</p>
{createPortal(
<p>This child in the document body html tag.</p>,
document.body
)}
</div>
);
}
Notice that the second heading is outside the box styling that the div has. Thus proving that the second heading is outside the div and in the document body tag
If you inspect element the div you will see that the second element is outside the div and inside the body tag. The portal transported the element from the div to the body.
Dead Simple Chat allows you to easily add Chat to any React Application using powerful Javascript Chat SDK.
Example 2: Rendering a Modal dialog with Portal
This is an advanced example: we have a modal dialog that is inside a container with overflow: hidden property on it and other styling that is interfering with the dialog
and we need to transport the dialog above everything else and it should float above the rest of the page
I will explain what is happening just scroll below the code:
So, in this example. let us look at each of the file individually
App.js Here we are importing the NoPortalExample and the PortalExample components
- inside the App.js there are two div elements with class clipping-container given to each of them
modalContent.js : We have a ModalContent component that receives an onClose
function on prop which will be called when the user clicks the close button
NoPortalExample.js We have a showModal
to determine modal should be shown or not
when the showModal without a button is clicked, the state of the showModal is set to true
ModalContent component is rendered directly as a child of the NoPortalExample when the showModal component is set to true
PortalExample.js When the Show Modal using Portal button is clicked the state of the showModal is set to true
ModalComponent is rendered using the Portal. the portal function is given the jsx for the Modal that is to be rendered and the DOM element where it is to be rendered that is the document body element
Dead Simple Chat allows you to easily add Chat to any React Application using powerful Javascript Chat SDK.
Example 3: Non-React Server Markup: Rendering React components using Portal
Portals can be useful if your React Root is only part of the static or server-rendered page that is not completely built with react
For example If you application is built with NodeJs
and express you can create some areas of interactivity within areas of that are static such as sidebars
Portals let you treat the app as a single React tree with shared state even though its parts render to different parts of the DOM.
import { createPortal } from 'react-dom';
const sidebarContentEl = document.getElementById('sidebar-content');
export default function App() {
return (
<>
<MainContent />
{createPortal(
<SidebarContent />,
sidebarContentEl
)}
</>
);
}
function MainContent() {
return <p>Part 1 will not be transported</p>;
}
function SidebarContent() {
return <p>This 2 part will be transported </p>;
}
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
);
<!DOCTYPE html>
<html>
<head><title>My app</title></head>
<body>
<h1>Welcome to my 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>
What are we doing here
we are creating a const sidebarContentEl
and assigning it the value of sidebar-content id which is a div assigned in the index.html file
then we are calling the createPortal
function with the props of SideBarContent and sidebarContenEl
that will transport the SideBarContent component to the sidebar div that is inside the div with the class of sidebar
Dead Simple Chat allows you to easily add Chat to any React Application using powerful Javascript Chat SDK.
Example 4: Rendering React components in to non react dom nodes
DOM nodes that are outside of react can also be managed using the react portal method.
We have a non react map widget and we want to have a pop up with react content on top of it.
const [popupContainer, setPopupContainer] = useState(null);
When creating third party widget always remember to store the DOM node that is returned by the widget.
So that you can render it.
useEffect(() => {
if (mapRef.current === null) {
const map = createMapWidget(containerRef.current);
mapRef.current = map;
const popupDiv = addPopupToMapWidget(map);
setPopupContainer(popupDiv);
}
}, []);
this lets you render react content using portal into the popupContainer.
return (
<div style={{ width: 250, height: 250 }} ref={containerRef}>
{popupContainer !== null && createPortal(
<p>Hi where are we!</p>,
popupContainer
)}
</div>
);
Here is the complete code:
Conclusion
In this article I have explained how the react Portal works. It transports the elements from one part of the DOM to another
It really is an handy tool when developing applications that have complex components on top of one another and you need to transport the component form one part of the DOM tree to another
Remember though the component is still part of the react tree and the events will still bubble up the tree. If you do not want the events to bubble up implement stop propagation in the component that is being sent via the portal
Thanks for reading