Skip to content

How Lazy Loading YouTube Videos Boosted Mobile Performance by 39%

YouTube embeds hide a performance secret: they load Google Fonts and JavaScript on every page load, even when users never watch the videos. On a recent client project with multiple video embeds, this created a page load delay that tanked mobile performance. Here's how we used lazy loading to boost PageSpeed scores from 56 to 78—and the complete Craft CMS implementation guide.

I was recently working on improving a client’s site performance. A pretty well-trafficked site with multiple embedded YouTube videos. There was a pretty significant bottleneck: YouTube embeds load Google Fonts and JavaScript before users even want to watch a video. 😱

The Problem: YouTube's Hidden Performance Cost

YouTube embeds seem innocent enough. You paste the iframe code, videos appear, everyone is happy. But little did I know about what’s happening behind the scenes on every page load:

  • YouTube’s player loads its own Google Fonts
  • Multiple font files download from fonts.gstatic.com
  • JavaScript libraries initialize for video controls
  • And... all of this happens even if users never watch the videos

PageSpeed Insights revealed the issue pretty clearly: a network dependency chain where fonts were blocking other critical resources, tanking our client’s mobile performance score.

What’s worse, the problem compounds with multiple videos. Each additional YouTube embed on a page multiplies these requests. This particular page had four videos, which meant four separate font downloads, four JavaScript initializations, and significantly more bandwidth consumption before users would see any content.

The Solution: Lazy Loading with YouTube Thumbnails

Instead of loading full YouTube players immediately, I implemented lazy loading "system", if you will, that:

  1. Shows YouTube's own thumbnail images (fast, lightweight)
  2. Displays a custom play button overlay
  3. Loads the actual video player only when clicked

The Results

Before: Mobile PageSpeed score of 56
After: Mobile PageSpeed score of 78
Improvement: 22-point increase (39% relative improvement)

While those numbers are pretty good, something to note is that the perception of the initial page load became noticeably faster. Sometimes that’s more important than raw number improvements.

Implementation in Craft CMS

This site, like so many of our client sites, runs on Craft CMS (version 5 to be specific). The videos are added using the oEmbed field plugin within a Matrix field. Here’s how I implemented the solution:

Step 1: Update Your Twig Template

Before: Standard video embed loop

{% set videosSection = entry.videosSection.all() ?? null %}

{% if videosSection %}
    <section class="videos">
        <div class="container">
            {% for video in videosSection %}
                <div class="video-block">
                    {{ video.videoEmbed|raw }}
                </div>
            {% endfor %}
        </div>
    </section>
{% endif %}

After: Lazy loading version

{% set videosSection = entry.videosSection.all() ?? null %}

{% if videosSection %}
    <section class="videos">
        <div class="container">
            {% for video in videosSection %}
                <div class="video-block">
                    {# Extract YouTube ID from iframe src #}
                    {% set videoId = video.videoEmbed|split('embed/')[1]|split('?')[0] %}
                    
                    {# Create lazy-loading YouTube thumbnail #}
                    <div class="lazy-youtube" data-video-id="{{ videoId }}">
                        <img 
                            src="https://img.youtube.com/vi/{{ videoId }}/maxresdefault.jpg" 
                            alt="YouTube video thumbnail"
                            loading="lazy"
                        >
                        <button class="youtube-play-btn" aria-label="Play video">
                            <svg width="68" height="48" viewBox="0 0 68 48">
                                <path d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z" fill="#f00"></path>
                                <path d="M 45,24 27,14 27,34" fill="#fff"></path>
                            </svg>
                        </button>
                    </div>
                </div>
            {% endfor %}
        </div>
    </section>
{% endif %}

Note: The .videos section and .container div aren't required for the functionality—adapt the structure to fit your existing markup. The .video-block class is simply a CSS grid container for layout purposes.

Step 2: Add the CSS

.lazy-youtube {
    position: relative;
    display: inline-block;
    cursor: pointer;
    width: 560px;
    max-width: 100%;
    aspect-ratio: 16/9;
    background: #000;
}

.lazy-youtube img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: opacity 0.3s ease;
}

.lazy-youtube:hover img {
    opacity: 0.8;
}

.youtube-play-btn {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    transition: transform 0.3s ease;
}

.youtube-play-btn:hover {
    transform: translate(-50%, -50%) scale(1.1);
}

.youtube-play-btn svg {
    filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));
}

Step 3: Add the JavaScript

document.addEventListener('DOMContentLoaded', function() {
    const lazyYouTubeElements = document.querySelectorAll('.lazy-youtube');
    
    lazyYouTubeElements.forEach(function(element) {
        const videoId = element.dataset.videoId;
        const playButton = element.querySelector('.youtube-play-btn');
        
        playButton.addEventListener('click', function() {
            // Create iframe
            const iframe = document.createElement('iframe');
            iframe.src = `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&rel=0`;
            iframe.width = '560';
            iframe.height = '315';
            iframe.frameBorder = '0';
            iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
            iframe.allowFullscreen = true;
            iframe.title = 'YouTube video player';
            
            // Replace thumbnail with iframe
            element.parentNode.replaceChild(iframe, element);
        });
    });
});
  • Eliminates unnecessary requests: No Google Fonts, no YouTube JavaScript until needed
  • Uses YouTube's CDN: Thumbnails load quickly from YouTube's optimized servers
  • Maintains functionality: Videos work exactly the same once clicked
  • Better privacy: Uses youtube-nocookie.com for the actual embeds
  • Cross-browser compatible: Works on all modern browsers

When to Use This Technique

This lazy loading approach is most beneficial when you have:

  • Multiple YouTube videos on a single page
  • Videos below the fold that users might not see
  • Mobile-heavy traffic where performance matters most
  • Content-focused sites where reading speed is crucial

The Bottom Line

Four our client, this simple optimization delivered immediate, measurable, and noticeable results. Users now get faster page loads and content editors can continue using the same workflow in Craft without any changes to their process.

Sometimes the best performance wins come from questioning what we load by default versus what users actually need upfront. In this case, a thumbnail and a play button proved to be a much better user experience than full video embeds nobody was watching yet.


Working on a Craft CMS project that needs a performance boost? I'd be happy to help audit your site and implement optimizations that deliver real results. From initial assessment through implementation and testing, I can help you achieve the kind of performance improvements that make a difference for both users and search rankings.

Get in touch to discuss your project, or check out more of my work at Block 81.