React in React is best learned by connecting the rule to an interactive form or modal. Start with the smallest component or hook, observe the output, and then add one realistic constraint so the concept becomes practical.
The key habit for this lesson is to watch props, state, and rendered JSX as it changes. That makes the topic easier to debug, easier to explain in interviews, and easier to use in real code without memorizing isolated syntax.
Portals let you render part of a React component into a different place in the DOM. Normally, a component renders inside its parent element. A portal allows that same component to stay in the React tree while its HTML is inserted somewhere else, such as directly inside document.body or a separate DOM node like #modal-root.
This is useful when a UI element needs to visually escape its container. For example, a modal should appear above the whole screen, a tooltip should float over other elements, and a dropdown should not be cut off by a parent with overflow: hidden.
Even though the portal content is rendered elsewhere in the DOM, React still treats it as part of the same component tree. That means props, state, context, and event bubbling still work the React way.
React provides portals through createPortal from react-dom. It takes two main values:
The component is still written and managed in the same React app, but its output is mounted into a different DOM node.
import { createPortal } from 'react-dom'
function Example() {
return createPortal(
<div>This content is rendered somewhere else in the DOM.</div>,
document.getElementById('portal-root')
)
}
React portals are easiest to manage when the DOM has a second mount point beside the main root. Keeping modal or tooltip content in a separate container avoids z-index fights and lets the rendered tree stay where React expects it while the element appears elsewhere in the DOM.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React App</title>
</head>
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>
</html>
A modal is the most common portal example. If you render a modal inside a deeply nested component, it may be affected by the parent layout. Rendering it through a portal avoids those problems and lets the modal sit above the whole page.
import { createPortal } from 'react-dom'
import { useEffect } from 'react'
function Modal({ isOpen, onClose, title, children }) {
useEffect(() => {
if (!isOpen) return
const handleKeyDown = (event) => {
if (event.key === 'Escape') {
onClose()
}
}
document.addEventListener('keydown', handleKeyDown)
document.body.style.overflow = 'hidden'
return () => {
document.removeEventListener('keydown', handleKeyDown)
document.body.style.overflow = ''
}
}, [isOpen, onClose])
if (!isOpen) return null
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-box" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h2>{title}</h2>
<button onClick={onClose}>Close</button>
</div>
<div className="modal-body">{children}</div>
</div>
</div>,
document.getElementById('modal-root')
)
}
export default Modal
import { useState } from 'react'
import Modal from './Modal'
function App() {
const [open, setOpen] = useState(false)
return (
<div style={{ overflow: 'hidden', height: '220px' }}>
<h2>Portal Example</h2>
<p>This container has limited space, but the modal will still appear correctly.</p>
<button onClick={() => setOpen(true)}>Open Modal</button>
<Modal isOpen={open} onClose={() => setOpen(false)} title="Delete Item">
<p>Are you sure you want to delete this item?</p>
<button onClick={() => setOpen(false)}>Cancel</button>
<button onClick={() => setOpen(false)}>Delete</button>
</Modal>
</div>
)
}
Tooltips are another great use case for portals. A tooltip often needs to appear above nearby content and ignore clipping from parent elements. With a portal, the tooltip can be positioned visually where it belongs without being trapped by the local layout.
import { createPortal } from 'react-dom'
import { useRef, useState } from 'react'
function Tooltip({ text, children }) {
const [visible, setVisible] = useState(false)
const [position, setPosition] = useState({ top: 0, left: 0 })
const triggerRef = useRef(null)
const showTooltip = () => {
const rect = triggerRef.current.getBoundingClientRect()
setPosition({
top: rect.top + window.scrollY - 36,
left: rect.left + window.scrollX + rect.width / 2
})
setVisible(true)
}
return (
<>
<span
ref={triggerRef}
onMouseEnter={showTooltip}
onMouseLeave={() => setVisible(false)}
>
{children}
</span>
{visible && createPortal(
<div
style={{
position: 'absolute',
top: position.top,
left: position.left,
transform: 'translateX(-50%)',
background: '#222',
color: '#fff',
padding: '6px 10px',
borderRadius: '4px',
fontSize: '12px',
whiteSpace: 'nowrap',
zIndex: 9999
}}
>
{text}
</div>,
document.body
)}
</>
)
}
One thing that surprises beginners is that a portal changes where content appears in the DOM, but it does not remove it from the React tree. That leads to several important behaviors:
For example, if a button inside a portal is clicked, React event handling can still reach a parent component higher in the React tree even though the DOM nodes are separated.
React portals solve a visual placement problem. They let you keep a component in the same React hierarchy while rendering its HTML somewhere else in the DOM. This is especially useful for UI elements that must appear above the rest of the page or escape container layout restrictions.
Once you understand createPortal, you can build modals, tooltips, overlays, and other floating UI in a cleaner and more reliable way.
Use React when the program needs a clear answer to a specific problem, not because the keyword looks familiar. In a real React task, first name the input, then name the transformation, then name the output. This small discipline shows whether the topic is being used correctly or only copied from an example.
A reliable practice flow is: create the smallest working component or hook, add one normal case, add one edge case such as nested modals and Escape-key closing, and then confirm the result with React DevTools and test output. If the result surprises you, reduce the code until the behavior is visible again.
The most common trap here is moving the DOM node but forgetting focus and cleanup. Avoid it by writing one sentence before the code that explains why React is the right choice. After the code runs, verify the lesson by doing this: inspect the DOM target and tab order.
Moving the DOM node but forgetting focus and cleanup.
Write the expected behavior first, then make the example prove it.
Practicing only the perfect input.
Also test nested modals and Escape-key closing before considering the lesson complete.
Looking only at the final output.
Trace props, state, and rendered JSX through each important step.
Use it when the problem matches the behavior shown in the example and when the result can be verified through React DevTools and test output.
Start with a tiny case, then test nested modals and Escape-key closing. The main warning sign is moving the DOM node but forgetting focus and cleanup.
Trace props, state, and rendered JSX, predict the result, run the example, and compare your prediction with the actual output.
Explore 500+ free tutorials across 20+ languages and frameworks.