Navigation transitions and the Portals proposal
Fast page loads are only part of the UX equation. The transition between pages is just as visible—and often worse than the load itself. Multi-page applications (MPAs) force users to stare at a blank screen while the next document is fetched and rendered. Single-page applications (SPAs) replace that with smooth transitions, but at the cost of significantly more build complexity.
The proposed Portals API targets that gap: it aims to give MPAs the transition polish of SPAs without the architectural overhead. A <portal> behaves like an <iframe> in that it embeds another page, but unlike an <iframe>, it can be activated to navigate into that embedded content.
Basic usage and feature detection
A minimal implementation is surprisingly compact. The following example creates a portal, appends it to the document, and activates it with animation:
When the activate() call fires, the embedded page takes over the current tab. This produces a navigation with no blank intermediate state—the destination has already been rendered inside the portal element.
For feature detection, which is useful for progressive enhancement, check for the HTMLPortalElement constructor:
// Adding some styles with transitions
const style = document.createElement('style');
style.innerHTML = `
portal {
position:fixed;
width: 100%;
height: 100%;
opacity: 0;
box-shadow: 0 0 20px 10px #999;
transform: scale(0.4);
transform-origin: bottom left;
bottom: 20px;
left: 20px;
animation-name: fade-in;
animation-duration: 1s;
animation-delay: 2s;
animation-fill-mode: forwards;
}
.portal-transition {
transition: transform 0.4s;
}
@media (prefers-reduced-motion: reduce) {
.portal-transition {
transition: transform 0.001s;
}
}
.portal-reveal {
transform: scale(1.0) translateX(-20px) translateY(20px);
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
`;
const portal = document.createElement('portal');
// Let's navigate into the WICG Portals spec page
portal.src = 'https://wicg.github.io/portals/';
// Add a class that defines the transition. Consider using
// `prefers-reduced-motion` media query to control the animation.
// https://developers.google.com/web/updates/2019/03/prefers-reduced-motion
portal.classList.add('portal-transition');
portal.addEventListener('click', (evt) => {
// Animate the portal once user interacts
portal.classList.add('portal-reveal');
});
portal.addEventListener('transitionend', (evt) => {
if (evt.propertyName == 'transform') {
// Activate the portal once the transition has completed
portal.activate();
}
});
document.body.append(style, portal);
Trying Portals in Chrome
Portals are available for experimentation in Chrome 85 and later behind an experimental flag:
- Enable
about://flags/#enable-portalsfor same-origin navigations. - For cross-origin tests, also enable
about://flags/#enable-portals-cross-origin.
Since this is an early experiment, it is recommended to run Chrome with a separate --user-data-dir flag when testing. Once enabled, you can verify the feature is active in DevTools:
Developers can use the embed and activation APIs together immediately. Getting the page content ready for a portal on load and activating it after an animation is all that is required. Try it live at uskay-portals-demo.glitch.me:
- Enter a URL you want to preview.
- The page is embedded as a
<portal>element. - Click on the preview.
- The preview is activated after a transition animation.
The <portal> element can also be styled when it is embedded, allowing sites to show the incoming page framed as a card or preview before activation.
Key parts of the API
The spec, which is being discussed in the Web Incubation Community Group (WICG), revolves around three main interfaces:
- The
<portal>element: An HTML element with asrcattribute, anactivatefunction, and apostMessageinterface for communication with the embedded page. Theactivatecall accepts an optional argument used to pass data to the portal on activation. - The
portalHostinterface: Exposed on thewindowobject, it tells the embedded document whether it is being displayed inside a<portal>and offerspostMessagefor communicating back to the host page. - The
PortalActivateEvent: Fired when a portal is activated. ItsadoptPredecessor()method retrieves the previous page as a<portal>, letting the newly activated page embed the outgoing document and create fully mutating, composed transitions between two pages.
Use cases beyond basics
The key scenarios at the WICG repository describe a broad set of patterns beyond navigation. For instance, a product listing page could pre-render the best-selling product’s detail page so it is ready for instant activation. The parent page can also receive a postMessage from its portal to reach exclusive settings on the detail page. The activated page can, in turn, render the departed listing page as a portal, reusing the same embedded pattern to support back navigation and user flows.
A differentiating factor from <iframe>-based previews is that Portals work across origins. Two independent sites that link to one another can use the API to set up seamless transitions between them. This cross-origin capability also applies inside SPAs.
Detailed developer documentation, the full API surface, and the community discussion are all available in the Portals spec repo. Feedback and feature requests are welcome through the same repository while the API is still in its experimental phase.



