Skip to content

YouTube embeds are heavy. A facade fixes that.

· 3 min read

A YouTube video on a page looks harmless. It's one iframe. But that iframe starts loading the full YouTube player as soon as the page opens, whether anyone presses play or not.

I was reading through Paul Irish's lite-youtube-embed and wanted to understand what it actually changes. This post is my notes.

What a normal embed costsLink to section: What a normal embed costs

The standard embed is an iframe pointing at youtube.com/embed/.... Inside that frame, YouTube loads its player JavaScript, styles, fonts and a few tracking requests. None of it is needed until someone clicks play, and most visitors never do.

Here is a one-second slice of a Chrome performance trace with a normal embed, from the project's README:

Chrome DevTools performance trace of a page with a normal YouTube iframe. In one second: 502 ms scripting, 159 ms rendering, 277 ms idle.
Normal iframe embed. 502 ms of scripting in one second. Screenshot from the lite-youtube-embed README.

Half of that second goes to running YouTube's JavaScript. The main thread is busy, so if the user tries to scroll or tap something in that window, the page feels slow.

What lite-youtube-embed doesLink to section: What lite-youtube-embed does

It replaces the iframe with a custom element:

html
<lite-youtube videoid="ogfYd705cRs" playlabel="Play video"></lite-youtube>

On load, the element only shows the video's thumbnail and a play button that looks like YouTube's. That's an image and a bit of CSS. When the user moves the pointer over it, it opens connections to YouTube's servers early. When they click, it swaps in the real iframe and the video starts.

This pattern is called a facade. You show something that looks like the heavy component and load the real thing only when someone needs it.

Same slice of the trace, with the facade:

Chrome DevTools performance trace of a page with lite-youtube. In one second: 9 ms scripting, 120 ms rendering, 803 ms idle.
lite-youtube facade. 9 ms of scripting. The only network request is the thumbnail. Screenshot from the lite-youtube-embed README.

Scripting drops from 502 ms to 9 ms, and the main thread is idle most of the time. The only thing the network panel shows is hqdefault.jpg, the thumbnail.

A couple of other details I liked:

  • It uses youtube-nocookie.com, so YouTube doesn't set cookies until the video plays.
  • The element can be in the HTML before the script loads. Without JavaScript, you can still link to the video.

The trade-offLink to section: The trade-off

The real player isn't there until the click, so things like autoplay on page load or controlling the player from your own code need extra work. The library has a js-api attribute for the second case. For a video in a blog post or on a landing page, I don't see a downside.

In Next.jsLink to section: In Next.js

You don't need to add the library by hand. @next/third-parties has a YouTubeEmbed component that uses lite-youtube-embed under the hood:

tsx
import { YouTubeEmbed } from "@next/third-parties/google";

<YouTubeEmbed videoid="ogfYd705cRs" />;

There are also ports for React and Vue, and the same idea exists for Vimeo and chat widgets like Intercom.

TakeawayLink to section: Takeaway

The interesting part for me isn't YouTube. It's the question behind it: does this third-party thing need to load now, or only when someone uses it? Chat widgets, maps and video players usually fall in the second group. Lighthouse even has an audit for it, "Lazy load third-party resources with facades".

Thanks to Paul Irish for the library and the screenshots.