Embedding YouTube Videos Without Impacting Web Perf

Improve the page loading performances caused by YouTube iframes with a few simple HTML and CSS tricks.

Yesterday I had to embed some YouTube videos in one of the articles I was writing about Raspberry Pi’s, and since I usually do not embed videos that often and rarely multiple of them, I never really upset about the performances penalty for one iframe, but after I added multiple of them I saw a massive web page loading degradation.

So it occurred to me how could I embed YouTube videos more efficiently. The first idea that came into my mind was lazy-loading, but that would require JavaScript and listen for scrolling events, so that idea turned me off immediately.

The next idea was how about just showing a link to the YouTube page with a video thumbnail inside. Now that looked more efficient to me.

So I started Duck Ducking the web for YouTube thumbnails API if there was any publicly available without registering for the dev program and all those hassles. To my surprise I found the answer immediately on the stack overflow question How do I get a YouTube video thumbnail from the YouTube API?.

Apparently, you can get a thumbnail image for any YouTube video from one of their subdomains like https://img.youtube.com or https://i.ytimg.com. Now, this probably isn’t the best way, since at any moment Google could change or limit access to these domains. So a more secure and proper way would be signing up for the Data API and have peace of mind, but that I wanted a quick solution so I accepted the risk for the moment.

Back to the YouTube video images! This answer on Stack Overflow has put a nice table on all sizes of images you can access:

  Width | Height | URL
  ------|--------|----
  120   | 90     | //i.ytimg.com/vi/<VIDEO ID>/1.jpg
  120   | 90     | //i.ytimg.com/vi/<VIDEO ID>/2.jpg
  120   | 90     | //i.ytimg.com/vi/<VIDEO ID>/3.jpg
  120   | 90     | //i.ytimg.com/vi/<VIDEO ID>/default.jpg
  320   | 180    | //i.ytimg.com/vi/<VIDEO ID>/mq1.jpg
  320   | 180    | //i.ytimg.com/vi/<VIDEO ID>/mq2.jpg
  320   | 180    | //i.ytimg.com/vi/<VIDEO ID>/mq3.jpg
  320   | 180    | //i.ytimg.com/vi/<VIDEO ID>/mqdefault.jpg
  480   | 360    | //i.ytimg.com/vi/<VIDEO ID>/0.jpg
  480   | 360    | //i.ytimg.com/vi/<VIDEO ID>/hq1.jpg
  480   | 360    | //i.ytimg.com/vi/<VIDEO ID>/hq2.jpg
  480   | 360    | //i.ytimg.com/vi/<VIDEO ID>/hq3.jpg
  480   | 360    | //i.ytimg.com/vi/<VIDEO ID>/hqdefault.jpg
  640   | 480    | //i.ytimg.com/vi/<VIDEO ID>/sd1.jpg
  640   | 480    | //i.ytimg.com/vi/<VIDEO ID>/sd2.jpg
  640   | 480    | //i.ytimg.com/vi/<VIDEO ID>/sd3.jpg
  640   | 480    | //i.ytimg.com/vi/<VIDEO ID>/sddefault.jpg
  1280  | 720    | //i.ytimg.com/vi/<VIDEO ID>/hq720.jpg
  1920  | 1080   | //i.ytimg.com/vi/<VIDEO ID>/maxresdefault.jpg

And in addition, all these images are also provided in WebP image format which is nice, the problem is it’s not available for images so it’s better to stick with jpg format.

HTML markup

Now that I can access the video thumbnails, I did quick markup of how the code would look like:

<div class="youtube">
  <a href="https://www.youtube.com/watch?v=VIDEO_ID" target="_blank">
    <figure>
      <img src="https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg" alt="VIDEO_TITLE" />
      <figcaption>VIDEO_TITLE</figcaption>
    </figure>
  </a>
</div>

But considering the different image sizes, we could easily make a responsive markup using the srcset property.

<div class="youtube">
  <a href="https://www.youtube.com/watch?v=VIDEO_ID" target="_blank">
    <figure>
      <img src="https://img.youtube.com/vi/VIDEO_ID/default.jpg"
           srcset="https://img.youtube.com/vi/VIDEO_ID/mqdefault.jpg 320w,
                   https://img.youtube.com/vi/VIDEO_ID/hqdefault.jpg 480w,
                   https://img.youtube.com/vi/VIDEO_ID/sddefault.jpg 640w,
                   https://img.youtube.com/vi/VIDEO_ID/hq720.jpg 1280w,
                   https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg 1920w"
           alt="VIDEO_TITLE" />
      <figcaption>VIDEO_TITLE</figcaption>
    </figure>
  </a>
</div>

CSS styling

My goal was to make it look as much as possible as the YouTube iframe without complicating the HTML markup too much. I managed to do that with some use of CSS pseudo-elements, for example, to add the shadow under the title of the video and make it more readable, and place the YouTube SVG icon from the beautiful Feather Icons as the play button.

.youtube {
  position: relative;

  a {
    text-decoration: none;
    
    &:before {
      content: '';
      width: 100%;
      height: 20%;
      position: absolute;
      top: 0;
      background-image: linear-gradient(#000, transparent);
    }

    &:after {
      content: "";
      background: url(https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/icons/youtube.svg)
        no-repeat;
      background-position: center;
      background-size: 100px 100px;
      position: absolute;
      top: 0;
      bottom: 0;
      right: 0;
      left: 0;
    }
  }
  
  img {
    width: 100%;
    height: auto;
  }

  figure {
    margin: 0;
  }

  figcaption {
    position: absolute;
    top: 10px;
    left: 10px;
    right: 10px;
    color: white;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
  }
}

The result

The result is very pleasing aesthetically and offering some big performance improvements at the same time, the only trade-off to this approach is that the visitors will not be able the play the video directly from the page but they will be redirected to the YouTube page, and I can live with that.

Take a look at the demo on Codepen, or visit this page on Dev & Gear to see it in production.

See the Pen An alternative way of embedding YouTube videos without impacting web perf by Bojan Vidanovic (@bojanvidanovic) on CodePen.

Conlusion

This technique at the end is a very simple one, and it can be easily implemented in any framework as a reusable component. I implemented it in Hugo as a shortcode for example. Anyway, I believe that every code has some room for improvement just like this one, so if you think this can be done better let me know in the comments.

19:42 (+0200)