How to embed YouTube and Vimeo the light way

Mads Stoumann - Jun 25 '22 - - Dev Community

You've probably embedded YouTube and Vimeo-videos dozens of times, using the standard <iframe>-embed-code.

While this works great out-of-the-box, services like Youtube loads a lot of Javascript, even if your users do not click on the embedded video.

I did a test with 3 embedded YouTube-videos. In this case, more than one megabyte of extra data was loaded.

That's a lot of unnecessary bandwidth. We can do better!


Lite Loaders

There are a lot of "lite loaders" out there, like lite-youtube or lite-youtube-embed.

If you're in a hurry, just npm install one of these.

Otherwise, hang on — it can be even lighter!


The Manual Way

We want to prevent YouTube or Vimeo loading anything, so we'll add the thumbnail-image for the video manually.

YouTube has an image-service, ytimg.com:

<img loading="lazy" src="https://i.ytimg.com/vi/[VIDEOID]/hqdefault.jpg">
Enter fullscreen mode Exit fullscreen mode

The important part here is the [VIDEOID]. Replace this with the actual id.


Vimeo does not have a service like this, but we can use the (free for now) Vumbnail-service:

<img loading="lazy" src="vumbnail.com/[VIDEOID].jpg">
Enter fullscreen mode Exit fullscreen mode

Vumbnail detects the video-provider automatically from the id/src.

Cool! Now, let's add a wrapper around the image, where we'll also add a dummy <iframe> and a play-<button>:

<youtube-embed>
  <img loading="lazy" src="https://i.ytimg.com/vi/[VIDEOID]/hqdefault.jpg" alt="Video Description">
  <iframe allow="autoplay" src="" data-src="https://www.youtube.com/embed/[VIDEOID]?autoplay=1"></iframe>
  <button aria-label="Play video"></button>
</youtube-embed>
Enter fullscreen mode Exit fullscreen mode

It does not have to be a custom element, it's just a bit more readable, and we can use <vimeo-embed> for Vimeo-videos.


Styling

For styling, we're going to use our custom elements tags directly, instead of class’es:

:is(vimeo-embed, youtube-embed) {
  aspect-ratio: 16 / 9;
  border-radius: var(--video-embed-bdrs, 0.25em);
  display: grid;
  inline-size: 100%;
  position: relative;
}
:is(vimeo-embed, youtube-embed) :is(iframe, img) { 
  block-size: 100%;
  border: 0;
  border-radius: inherit;
  inline-size: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
}
Enter fullscreen mode Exit fullscreen mode

For the play-button, we'll add a bunch of CSS Custom Properties — allowing us to easily change colors etc:

:is(vimeo-embed, youtube-embed) button {
  background-color: var(--button-bgc, #F00);
  block-size: var(--button-h, 50px);
  border: 0;
  border-radius: var(--button-bdrs, 14%);
  display: grid;
  inline-size: var(--button-w, 75px);
  opacity: var(--button-op, 0.8);
  position: absolute;
  place-self: center;
  transition: all .2s ease-in;
}
:is(vimeo-embed, youtube-embed) button::before {
  aspect-ratio: 1;
  background: #FFF;
  block-size: 1.5em;
  clip-path: polygon(20% 0%, 20% 100%, 100% 50%);
  content: '';
  place-self: center;
}

vimeo-embed button { --button-bgc: #00adef; }
Enter fullscreen mode Exit fullscreen mode

To hide the play-button when the <iframe> is loaded, we'll check if the src-attribute contains something else than an empty string:

:is(vimeo-embed, youtube-embed) iframe:not([src=""]) + button {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

Now, to load the real <iframe>, all we have to do is replace src with data-src:

document.querySelectorAll(':is(vimeo-embed, youtube-embed) button').forEach(button => button.addEventListener('click', () => {
  const video = button.previousElementSibling;
  video.src = video.dataset.src;
}))
Enter fullscreen mode Exit fullscreen mode

And that's it! We've replaced one megabyte of data with a few bytes.

Here's a Codepen-demo, with a short disclaimer: The videos don't autoplay directly from the Codepen-iframe, but will on your own site:


The Javascript Way

If you don't want to handle all that markup, a simpler way could be:

<youtube-embed id="5b4YcLB4DVI" title="Text">
<vimeo-embed id="70591644" title="Text">
Enter fullscreen mode Exit fullscreen mode

All we need is the id of the video and a title for the thumbnail alt-attribute. Then in JavaScript, we can create the <img> and <button>-elements:

document.querySelectorAll('vimeo-embed, youtube-embed').forEach(v => {
  const [poster, src] = v.tagName === 'VIMEO-EMBED' ?
    [`vumbnail.com/${v.id}.jpg`, 'player.vimeo.com/video'] :
    [`i.ytimg.com/vi/${v.id}/hqdefault.jpg`, 'www.youtube.com/embed'];

  v.innerHTML = `<img loading="lazy" src="https://${poster}" alt="${v.title}"><button aria-label="Play"></button>`;
  v.children[1].addEventListener('click', () => v.innerHTML = `<iframe allow="autoplay" src="https://${src}/${v.id}?autoplay=1"></iframe>`)
})
Enter fullscreen mode Exit fullscreen mode

That's 335 bytes gzipped, so it's not going to break your performance-budget.

Happy coding!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .