DEV Community

Cover image for How to embed YouTube and Vimeo the light way
Mads Stoumann
Mads Stoumann

Posted on

How to embed YouTube and Vimeo the light way

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!

Discussion (5)

Collapse
robole profile image
Rob OLeary • Edited on

I was going to look into this soon as well! I saw this article on lazy loading youtube vidoeos recently, How to lazy load YouTube videos with vanilla JavaScript , and wanted to investigate it.

One thing that people forget or dont know is that you can lazy load an iframewith loading=lazy (Firefox is a laggard in support however and Safari has it behind a flag). This will spare you the upfront cost of loading the video scripts and give a faster page load if you do have to go the iframe route.

Collapse
madsstoumann profile image
Mads Stoumann Author

Yes, good point! It's only Chrome for now, but Safari has lazy-load for iframes behind a flag in v16. Firefox status is unknown.

Collapse
robole profile image
Rob OLeary • Edited on

Yep. Let's hope Firefox follows suit soon.

Considering so many embedded videos are sourced from youtube, it is an important one IMO. I just try to avoid YouTube altogether.

I wrote about lazy-loading videos a while back, you can use preload="none" and the video file is only downloaded when the user hits play.

Collapse
jankapunkt profile image
Jan Küster

One question, is it still possible to trigger Autoplay (to omit the double clicking)? I think I've read some time ago that this is prevent by modern browsers now, right?

Collapse
madsstoumann profile image
Mads Stoumann Author

It works locally, but not from the Codepen (the small "disclaimer"). Maybe because Codepen embed is in itself an iframe? Not sure!