Load everything lazily with IntersectionObserver

January 23, 2019

Photo by Erik-Jan Leusink

Just after I've released 1.8 version of site and published post Implementing medium like tooltip, I had understood its page loads 6 mb! And it is absolutely inappropriate for my blazing fast site. The most significant change of this release was integration with codesandbox: all embedded links to it are transformed to iframes. Results I've got in network tab of chrome dev tools also had met my expectations: most of resources were loaded by iframes.

Obvious solution is to load iframes lazily - when they appear in the viewport. So here is a code I came up with:

First of all rename src attribute of iframe to something else (e.g. data-src), so it won't load anything during initial render.

<iframe
  data-src="https://nikitakirsanov.com"></iframe>

And insert src when iframe intersects with viewport:

// react hooks fits perfect for extracting
// such type (not only) of reusable logic
let useLazyIframe = () =>
  // we need to register our observer after our component mounted
  useEffect(() => {
    let observer = new IntersectionObserver(
      entries => {
        entries
          // filter only visible iframes
          .filter(e => e.isIntersecting)
          .forEach(({ target: $el }) => {
            // iframe will start loading now
            $el.setAttribute('src', $el.getAttribute('data-src'))
            // no need to observe it anymore
            observer.unobserve($el)
          })
      },
      {
        // we'll load iframe a bit beforehand
        rootMargin: '10%',
      }
    )

    document
      .querySelectorAll('iframe[data-src]')
      .forEach($el => observer.observe($el))

    // do not forget to clean up on component unmount
    return () => observer.disconnect()
  }, [/* we need to register observer once */])

That's it!

Read more:

Intersection Observer API

IntersectionObserver’s Coming into View

React hooks overview

Read next