Exploring HTMLPortalElement with React

HTMLPortalElement is a draft of a new HTML Element, very similar to iframes but with the big difference that it allows to navigate to the content of the "iframe" by using a page transition.

Example of HTMLPortalElement

To know more about it, I recommend to read these references:

In this article, I will explain how to use this future feature to do a "Hello world" demo with React.

Getting started

First of all, to use this draft feature you'll need Chrome Canary. Once you have it, activate the flag of Portals:

Portal flag in Chrome

Next, we'll test portals. Remember that portals need to be on the top level of our app (unlike it happens with iframes).

Hello world with HTMLPortalElement and React:

import React, { useState, useEffect, useRef } from 'react'
import { render } from 'react-dom'

function PortalExample() {
  if (!window.HTMLPortalElement) {
    return 'HTMLPortalElement is not supported in your browser.'
  }

  return <portal src="https://aralroca.com" />
}

render(<PortalExample />, document.getElementById('root'))

We get a similar result than using an iframe:

Devtools inspecting the portal

Nevertheless, we want a beautiful transition to navigate to the content of this page. How could we get this?

Navigating to a portal

As I said, there is a significant difference between portals and iframes; with portals we can navigate to the content. In order to do that, the element has the function activate to go to the page.
<portal
  src="https://aralroca.com"
  // navigate to content
  onClick={({ target }) => target.activate()}
/>

Now we can navigate to the content. Although without any transition... yet:

Using the portal

Adding a page transition

Instead of calling the activate function on the onClick event, we are going to use the onClick event to add an extra css class with the transition. Then, we are going to use the onTransitionEnd event to control when the css transition is finished. After that, we'll call the activate function.

Therefore, our css transition is going to scale the portal until the portal fits all the content of the page (width and height 100%).

React code:

import React, { useState } from 'react'
import { render } from 'react-dom'

import './style.css'

function PortalExample() {
  const [transition, setTransition] = useState(false)

  if (!window.HTMLPortalElement) {
    return 'HTMLPortalElement is not supported in your browser.'
  }

  return (
    <portal
      src="https://aralroca.com"
      className={`portal ${transition ? 'portal-reveal' : ''}`}
      onClick={() => setTransition(true)}
      onTransitionEnd={(e) =>
        e.propertyName === 'transform' && e.target.activate()
      }
    />
  )
}

render(<PortalExample />, document.getElementById('root'))

Styles:

body {
  background-color: #212121;
}

.portal {
  position: fixed;
  width: 100%;
  cursor: pointer;
  height: 100%;
  transition: transform 0.4s;
  box-shadow: 0 0 20px 10px #999;
  transform: scale(0.4);
}

.portal.portal-reveal {
  transform: scale(1);
}

Finally, we get the page transition in our portal:

Adding a transition to the portal

Code: https://github.com/aralroca/HTMLPortalElement-react-example

Benefits of portals

Portals are a new proposal to load pages as an iframe, allowing the navigation to the content with a beautiful transition and improving the user's experience.

They can be useful for previews of videos / audio, so you can navigate to the content page without stop watching / listening the media at any moment.

Final example using portals

Of course, here we are using a different origin (YouTube). Nevertheless, if we use the same origin, we can communicate with the portal at any moment and do things like displaying a beauty preview or loading the rest of the content after the portal is activated.

Conclusion

Portals are still a proposal and maybe it's something we won't see in the future. Whatever, if it finally exists, it's going to be useful to preview content, especially, for media.

References:

Discuss on Dev.toDiscuss on TwitterEdit on GitHub
More...