How to make facades for YouTube videos in Nuxt and TailwindCSS to optimize page loading speed

How to make facades for YouTube videos in Nuxt and TailwindCSS to optimize page loading speed

I recently added a YouTube video to my page and it negatively affected my website Page Insights (PI) performance. One of the tips PI provided was to provide facades for the Youtube video links, to avoid loading them directly. I implemented it and it indeed improved my PI result and sped up the page load. Below is an overview of how to do it.

What is a facade?

The facade is a thumbnail picture that looks like the actual video but only loads the actual component once the user

  • Mobile/Tablet:

    • clicks on the video
  • Desktop

    • hovers a mouse over the facade

How to implement it?

I found a couple of libraries online that do it, but none of them seemed to work for me, thus I decided to implement my own using Nuxt3 and TailwindCSS.

<template>
    <div class="flex justify-center mt-5">

        // DESKTOP
        <!-- This padding-top gives a 16:9 aspect ratio -->
        <div @mouseenter="showIframe = true"
            class="hidden sm:block relative overflow-hidden w-full" style="padding-top: 56.25%;">

            <!-- YOUTUBE SVG icon centered in front of the image -->
            <svg v-if="!showIframe" xmlns="http://www.w3.org/2000/svg" width="4.25em" height="4.25em" viewBox="0 0 256 180"
                class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-10">
                <path fill="#f00"
                    d="M250.346 28.075A32.18 32.18 0 0 0 227.69 5.418C207.824 0 127.87 0 127.87 0S47.912.164 28.046 5.582A32.18 32.18 0 0 0 5.39 28.24c-6.009 35.298-8.34 89.084.165 122.97a32.18 32.18 0 0 0 22.656 22.657c19.866 5.418 99.822 5.418 99.822 5.418s79.955 0 99.82-5.418a32.18 32.18 0 0 0 22.657-22.657c6.338-35.348 8.291-89.1-.164-123.134" />
                <path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z" />
            </svg>


            <img v-if="!showIframe" :src="'https://img.youtube.com/vi/' + extractYouTubeID(sanitizedLink) + '/maxresdefault.jpg'"
                class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover w-full h-full" />

            <iframe v-if="showIframe" :src="YOUTUBE_LINK" title="YouTube video player" frameborder="0"
                allowfullscreen class="absolute top-0 left-0 w-full h-full"></iframe>
        </div>


        // MOBILE
        <div @click="showIframe = true" class="block sm:hidden relative overflow-hidden w-5/6"
            style="padding-top: 56.25%;">

            <!-- YOUTUBE SVG icon centered in front of the image -->
            <svg v-if="!showIframe" xmlns="http://www.w3.org/2000/svg" width="4.25em" height="4.25em" viewBox="0 0 256 180"
                class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-10">
                <path fill="#f00"
                    d="M250.346 28.075A32.18 32.18 0 0 0 227.69 5.418C207.824 0 127.87 0 127.87 0S47.912.164 28.046 5.582A32.18 32.18 0 0 0 5.39 28.24c-6.009 35.298-8.34 89.084.165 122.97a32.18 32.18 0 0 0 22.656 22.657c19.866 5.418 99.822 5.418 99.822 5.418s79.955 0 99.82-5.418a32.18 32.18 0 0 0 22.657-22.657c6.338-35.348 8.291-89.1-.164-123.134" />
                <path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z" />
            </svg>

            <!-- Placeholder for the image -->
            <img v-if="!showIframe" :src="'https://img.youtube.com/vi/' + extractYouTubeID(sanitizedLink) + '/maxresdefault.jpg'"
                class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover w-full h-full" />

            <!-- The iframe will take up the full space when displayed -->
            <iframe v-if="showIframe" :src="YOUTUBE_LINK + '?autoplay=1&mute=1'" title="YouTube video player"
                frameborder="0" allowfullscreen autoplay loading="lazy"
                class="absolute top-0 left-0 w-full h-full"></iframe>
        </div>
    </div>
</template>

<script setup lang="ts">

const showIframe = ref(false)

function extractYouTubeID(url: string) {
    const regExp = /^.*(youtu.be\/|v\/|e\/|u\/\w+\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
    const match = url.match(regExp);

    if (match && match[2].length === 11) {
        return match[2];
    } else {
        return null;
    }
}
</script>

In the code above:

  • I use extractYoutubeID to extract a video ID from a provided link

  • Then I find a video thumbnail by using the video ID

    • as explained here on StackOverflow, I just used the one that gave the best impression, but you can use lower resolution thumbnail as well

    • I format it so it has the same size as the actual YT video.

    • I also added an SVG YT icon

  • Once the user hovers (desktop) over or clicks (mobile) the actual video is showed

    • On mobile video has autoplay activated, to give a better user experience, but some browsers block it

Summary

This way your videos won't load on initial page load and thus speed it up. It should also positively affect your Page Insights

If you have any comments or questions, please let me know.

Cheers!

Bart