Lazy load iframes processor

Roadmap Card

Description

Iframes can impact the loading time and performance of a site, so we can provide Frontity developers with a processor that makes them lazy load, similar to what we are doing for images.

User Stories

As a Frontity developer
I want to lazy load all my iframes
so that I don’t hurt my performance

Possible solution

We can release both an Iframe component that ads native lazy loading with fallback to intersection observer. The same we are doing for images.

@iamuchejude let me give you more context here and some ideas.

First, please note that Frontity needs Proxy to run. If the browser doesn’t support Proxy (old browsers like IE11) we simply don’t hydrate React: https://github.com/frontity/frontity/blob/dev/packages/core/src/client/index.tsx#L8-L60
The site still works because the HTML is there. If users click on a link, they get a new HTML from the server.

For lazy loaded components (images and iframes), we need to support five scenarios:

  1. JavaScript is disabled.
  2. Proxy is not supported.
  3. Proxy is supported, but neither the Intersection Observer or native lazy loading are.
  4. Proxy and Intersection Observer are supported, but native lazy load is not.
  5. Proxy, Intersection Observer and native lazy load are supported.
javascript Proxy Intersection Observer Native lazy load
1 No - - -
2 Yes No - -
3 Yes Yes No -
4 Yes Yes Yes No
5 Yes Yes Yes Yes

Scenario 1: No JavaScript

The HTML generated in the server (when state.frontity.rendering === "ssr") needs to be valid for the first scenario, because JavaScript won’t be able to change it once the browser loads.

The problem is that we don’t want it to load until we are able to control it with JavaScript. So the only way to do so that I know of so far is to use the noscript tag:

<noscript>
  <img src="..." ... />
</noscript>

<noscript>
  <iframe src="..." ... />
</noscript>

Scenario 2: No Proxy (no Frontity)

The JavaScript required to make it work in scenario 2 must be outside of Frontity, because if Proxy is not present, we won’t load Frontity.

That can be achieved adding a <script> tag to the HTML during SRR that only runs if Proxy is not supported. Let’s call it the no-Proxy script:

<script>
if (!("Proxy" in window)) {
  // Do required modifications...
}
</script>

We have two valid tactics here:

<!-- From this -->
<noscript>
  <img src="..." />
</noscript>
<!-- to this -->
<img src="..." />

<!-- or from this -->
<img data-src="..." />
<!-- to this -->
<img src="..." />

I like the idea of reusing the noscript tag. It looks more clean, we only have one tag, and maybe we can reuse the no-Proxy script for images, iframes and even scripts if we add the same class to all of them.

<noscript class="no-proxy-load">
  <img src="..." />
</noscript>

<noscript class="no-proxy-load">
  <iframe src="..." />
</noscript>

<noscript class="no-proxy-load">
  <script src="..." />
</noscript>

<noscript class="no-proxy-load">
  <script>
    // code...
  </script>
</noscript>

<script>
  if (!("Proxy" in window)) {
    document.querySelector(".no-proxy-load");
    // remove noscript tags...
  }
</script>

Scenario 3: Proxy, but no IO

At this point we can use React instead of plain JavaScript because Frontity will load.

If there is no Intersection Observer, we simply load all the images right away. That means that once we are in the browser (state.frontity.rendering === "csr") we either remove the noscript tag or change data-src and data-srcset to src and srcset (depending on the approach we use).

At this point React also takes control of future pageviews. That means we are going to be generating new images for those pages. In this scenario we simply need to generate the normal images/iframes, no noscript tag or data-src is needed.

Scenario 4: IO, but no native lazy load

This scenario is similar to the previous one, but here we don’t have to change the images right away, we have to wait until the user scrolls down. We can use the react-intersection-observer hook for that.

In this scenario we need maintain the noscript tag or the data-src until inView is true
even for new images generated in client side rendering.

Scenario 5: Native lazy load

Finally, if native lazy load is available, we simply let the browser decide for itself. This means that, if I’m not mistaken, this scenario is the same than the scenario 3 as long as we add the loading attribute to the images/iframes.

<!-- From this -->
<noscript>
  <img src="..." loading="lazy" />
</noscript>
<!-- to this -->
<img src="..." loading="lazy" />

The loading attribute is ignored by older browsers, so I don’t see a reason why not to add it.

It’s also the same case than scenario 3 for new images/iframes. We add normal ones and let the browser decide when to load them.

Images with no height

There is a problem with images without a height attribute and native lazy load: as far as I know, they do not lazy load.

To solve that in the current implementation of the Image component we decided to use the intersection observer instead of the native lazy load if height is missing.

That means the scenarios for images would be like this:

javascript Proxy Intersection Observer Native lazy load AND height
1 No - - -
2 Yes No - -
3 Yes Yes No -
4 Yes Yes Yes No
5 Yes Yes Yes Yes

I don’t think this applies to iframes because they have a default height of 150px if the height attribute is missing.


@iamuchejude please add in this topic your progress, questions and findings as you work though this :slight_smile:

@luisherranz Thanks for the info.

1 Like