
Portals Are a Polyfill
Discover how the new native Top Layer API renders years of z-index battles and complex React portal workarounds completely obsolete.
Portals Are a Polyfill
You’ve been told for years that React Portals are the "standard" way to handle modals and tooltips. We’ve collectively accepted that if you want a component to appear on top of everything else, you have to physically teleport its DOM node to the end of the <body> tag. But the truth is, Portals were never a feature; they were a desperate workaround for CSS limitations. If you’re still reaching for createPortal as your first instinct, you’re essentially shipping a manual polyfill for a native browser feature that is finally here.
The Stacking Context Trauma
We’ve all been there. You build a beautiful modal component, nest it inside a sidebar, and suddenly half the modal is cut off because the sidebar has overflow: hidden. Or worse, the modal appears *behind* a header because of a z-index battle you lost three months ago.
To fix this, we turned to Portals. We told the browser: "I know this component logically lives inside this div, but please render it over here at the bottom of the document so it doesn't get squashed."
It worked, but it sucked. It broke event delegation in weird ways, made accessibility (like focus traps) a nightmare to coordinate, and forced us to manage a completely separate DOM tree for "floating" things.
The New Reality: The Top Layer
The browser finally realized that "appearing on top" is a fundamental requirement of UI development. Enter the Top Layer API.
The Top Layer is a special internal plane in the browser that sits above every other element in your document. It doesn't care about z-index. It doesn't care about overflow: hidden. It doesn't care if your parent element has transform: scale(0.5). If an element is in the Top Layer, it renders on top. Period.
The best part? You don't need a library to use it. You just need the <dialog> element or the popover attribute.
No More Teleporting
Here is how we used to handle a modal in React. It’s a lot of ceremony just to get a box to show up:
// The old, "portal" way
import { createPortal } from 'react-dom';
function LegacyModal({ isOpen, children }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay">
<div className="modal-content">{children}</div>
</div>,
document.getElementById('modal-root') // The "teleport" destination
);
}Now, look at the native way. This doesn't need a special root div. It stays exactly where you put it in your component tree, but the browser "hoists" the rendering for you:
// The modern, native way
import { useRef } from 'react';
function ModernModal() {
const dialogRef = useRef(null);
const openModal = () => dialogRef.current?.showModal();
const closeModal = () => dialogRef.current?.close();
return (
<>
<button onClick={openModal}>Open Modal</button>
{/* This stays right here in the DOM, but renders in the Top Layer */}
<dialog ref={dialogRef} className="my-modal">
<p>I am in the Top Layer!</p>
<button onClick={closeModal}>Close</button>
</dialog>
</>
);
}When you call .showModal(), the browser puts that <dialog> into the Top Layer automatically. No z-index: 999999 required.
The "Popover" Revolution
But what about tooltips, dropdowns, or menus? You don't always want a modal that dims the background. This is where the popover attribute comes in. It’s a global attribute that turns any element into a Top Layer inhabitant.
<button popovertarget="my-menu">Settings</button>
<div id="my-menu" popover>
<ul>
<li>Profile</li>
<li>Logout</li>
</ul>
</div>Zero JavaScript is required to toggle this. The browser handles the "light dismiss" behavior (closing when you click outside or hit Esc) and ensures it’s never clipped by a parent container.
Why This Matters
Why should you care about switching? It's not just about writing less code.
1. Accessibility by Default: The <dialog> element handles focus management for you. When it opens, focus moves inside. When it closes, focus moves back to the trigger. Doing this manually with Portals is a chore that most developers skip.
2. Context Preservation: When you use Portals, the element is physically moved. This can sometimes mess with React Context or CSS inheritance. With the Top Layer, the element stays in its logical place in the DOM, so it inherits styles and state exactly as you'd expect.
3. The End of Z-Index Wars: We can finally stop using z-index. Seriously. If it’s an overlay, it goes in the Top Layer. Everything else can just live in the natural document flow.
The Catch
Is there one? Browser support for <dialog> is excellent (all major browsers since 2022). The popover attribute is newer but already has support in Chrome, Edge, and Safari, with Firefox catching up rapidly.
If you're supporting IE11, sure, keep using Portals. But for the rest of us? It’s time to stop teleporting our DOM nodes and start using the platform. Portals were a brilliant bridge, but we've finally reached the other side.


