Technical

2 Minutes

TLDR: Click here for code to implement a lightweight, resilient clickable YouTube preview.

YouTube videos are expensive to load on a web page. This compounds if you want your page to display many videos at the same time for the user to choose from. Before the user even plays the video, it loads 4MB onto your previously zippy page.

Of course, YouTube does a decent job of caching these resources, but your browser is still doing all this work, creating another complete page context per video, executing all it’s JavaScript etc.

The ideal state is that an iframe is only created when the user plays the video, so if you’re listing dozens of videos that can be played in place you only pay the performance penalty when the user actually requests the video to play.

A solution … that YouTube makes brittle

One solution is to first show a preview image using a thumbnail image from the video, and when that is clicked, swapping it out for the iframe. This works great! … until it doesn’t.

The Problem

The url for a thumbnail image looks like https://img.youtube.com/vi/bJ7mTY9501c/maxresdefault.jpg . Note the maxresdefault.jpg at the end, this specifies the highest quality possible preview image. The problem is that this only exists on some videos and not others.

It should be easy to add an onerror handler though right? Then we can fall back to a lower res preview image. Haha, yeah, but no.

Instead of simply failing to return the image, sending a 404 response header and causing the <img /> element to trigger it’s onerror listener, YouTube both sends a 404 response and sends a small ugly placeholder image that makes your site look broken. This triggers the onload listener instead of the the expected onerror listener.

The Solution

Instead of an onerror , add an onload handler to the <img /> element, and check the size of the image. The ugly fallback image is 120px wide and 90px tall. Check the size of the image, and if the fallback image is detected, iterate through the available thumbnail images until one loads. The image below shows an implementation of this.

Full Example

There’s a full no-framework example at https://chofter.com/examples/youtubePreview.html , view the source for the full example. This can be easily adapted for any framework like React, Vue etc.

Prior Art

Paul Irish describes another solution to this problem that I found after I built this, check it out here. That also points to these three implementations that are also worth perusing.

I like mine as it uses the least code and is trivial to implement in whatever framework you like, but another of these might suit your needs or taste better.

Unknown's avatar

Published by Shane O'Sullivan

I am a software engineer and manager from Ireland. I spent 7 years working in Ireland from 2003 – 2010, then ten years in Silicon Valley from 2010 to 2020. In California I spent about 6.5 years at Facebook Engineering, the last three of which I was an engineering manager in the Ads organisation focusing on customer facing products for creating and managing ads. At Stripe I built the Developer Productivity organisation, with teams that were responsible for the use of the Ruby language, testing infrastructure, documentation, developer tooling (e.g. IDE integrations) and more. At Promise, I was Head of Engineering from 2018 – 2020, responsible for building the first few iterations of our products, hiring for all product roles, meeting with clients and investors, and anything else needed to get a tiny startup bootstrapped and successful. Now I’m back in Ireland, working various projects while advising the great people at Stainless (stainless.com). This blog contains my various musings on all things technical/interesting on the interweb and beyond.

Published