Bootcamp
Search…
📅
Course Schedule
0: Language and Tooling Teaching Guide
1: Frontend Basics Teaching Guide
2: Backend Basics Teaching Guide
3: Backend Applications Teaching Guide
4: Backend Structure Teaching Guide
5: Full-Stack Applications Teaching Guide
6: Frontend Infrastructure Teaching Guide
7: React Teaching Guide
8: Advanced React Teaching Guide
9: Advanced Topics Teaching Guide
🧮
Algorithms Teaching Guide
💼
Interview Prep Teaching Guide
☺
User Experience Teaching Guide
8.7: React Portals
Sometimes, especially in a non-CRA environment we are rendering a React app into a page that has already existing HTML.
An example would be an EJS rendered page that already has a nav bar in it.
In cases like these we may still want to control elements of the React app outside of the root div.
According to the docs, React portals: provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
In this example we have a Bootstrap nav bar that is setup outside of the react app. We want to render a message notification icon outside the react app, into the Bootstrap nav bar.
First we edit the navbar so that we have an empty element in the nav bar with an id:

index.html

1
<nav class="navbar navbar-expand-lg navbar-light bg-light">
2
<div class="container-fluid">
3
<a class="navbar-brand" href="#">Navbar</a>
4
<span id="message-alert-root"></span>
5
...
Copied!

App.js

In our main React app we create a portal component that will render elements into message-alert-root.
1
import "./styles.css";
2
​
3
import { useRef, useState, useEffect } from 'react';
4
import { createPortal } from 'react-dom';
5
​
6
// this is the id of the element in the index.html file
7
const id = 'message-alert-root';
8
​
9
const MessagePortal = ({messageCount}) => {
10
// get a hold of a DOM element
11
const rootElemRef = useRef(document.createElement('div'));
12
​
13
useEffect(function() {
14
// Look for existing target dom element to append to
15
const parentElem = document.querySelector(`#${id}`);
16
// Add the detached element to the parent
17
parentElem.appendChild(rootElemRef.current);
18
// This function is run on unmount
19
return function removeElement() {
20
rootElemRef.current.remove();
21
};
22
}, [id]);
23
24
let messageAmount = null;
25
if (messageCount > 0) {
26
messageAmount = <span className="message-amount">{messageCount}</span>;
27
}
28
​
29
const messageIcon = (
30
<span className="message-portal-icon">
31
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat-left-text" viewBox="0 0 16 16">
32
<path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H4.414A2 2 0 0 0 3 11.586l-2 2V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12.793a.5.5 0 0 0 .854.353l2.853-2.853A1 1 0 0 1 4.414 12H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
33
<path d="M3 3.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3 6a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9A.5.5 0 0 1 3 6zm0 2.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/>
34
</svg>
35
{messageAmount}
36
</span>
37
);
38
​
39
// use the react createPortal function to append some
40
// elements into the DOM element we created above
41
return createPortal(messageIcon, rootElemRef.current);
42
};
43
​
44
export default function App() {
45
const [messageCount, setMessageCount] = useState(null);
46
return (
47
<div className="App">
48
<h1>Hello CodeSandbox</h1>
49
<h2>Start editing to see some magic happen!</h2>
50
<p><button onClick={e => setMessageCount(messageCount + 1)}>add message</button></p>
51
<MessagePortal messageCount={messageCount}/>
52
</div>
53
);
54
}
Copied!
See React documentation on Portals here.
Last modified 2mo ago
Copy link
Contents