Skip to content

Lazy load YouTube videos in WordPress (code snippet)

This code snippet enables lazy loading for YouTube videos on your WordPress site, improving load times by only loading videos when a user clicks them.

Watch tutorial on YouTube.

Benefits:

  • Increase page load speed of WordPress.
  • Save user and server bandwidth.
  • Use YouTube no-cookie domain. Prevent unintended tracking.
  • No need to make WP_HTTP API (default WordPress oEmbed feature) request to YouTube.  Saves your server processing resources.
  • Works with embed shortcode and plain video URL on its own line inside content and sidebar.
  • No need to clear the oEmbed cache. It’s not being used because the content is created instantly with basic PHP.
  • Open-source code that can be modified and used to fit your needs.

Code snippet to lazy load YouTube embeds

/* veppa_yt_lazy_embed https://veppa.com/lazy-load-youtube/ version: 1.0 */

/* convert single line youtube URL to embeds */
add_filter( 'the_content', 'veppa_yt_lazy_embed', 6); 

function veppa_yt_lazy_embed($content){ 
	// use plain iframe 
	// $yt_wrap = '';
	// use custom iframe wrapper from your current theme
	// $yt_wrap = '<div class="nv-iframe-embed my3">{content}</div>';
	// use snippet iframe wrapper
	$yt_wrap = '<div><div class="veppa_yt_lazy_embed">{content}</div></div><style>.veppa_yt_lazy_embed{position:relative;padding-bottom:56.25%;height:0;margin:1rem 0;} .veppa_yt_lazy_embed iframe{position:absolute;top:0;left:0;width:100%;height:100%;}</style>';

	// check if content has youtube url at all 
	if(strpos( $content,'youtube.com')!==false || strpos( $content,'youtu.be')!==false){
		$startpreg = microtime(true);

		/*
		default embed:
		<iframe title="video title" width="1200" height="675" src="https://www.youtube.com/embed/80yHeJjO4bQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

		detects following URL formats: 
		http://www.youtube.com/watch?v=80yHeJjO4bQ
		http://www.youtube.com/watch?v=80yHeJjO4bQ&feature=related
		http://youtu.be/80yHeJjO4bQ
		https://youtu.be/80yHeJjO4bQ
		http://www.youtube.com/embed/watch?feature=player_embedded&v=80yHeJjO4bQ
		http://www.youtube.com/watch?v=80yHeJjO4bQ
		http://youtu.be/80yHeJjO4bQ
		https://youtu.be/80yHeJjO4bQ?t=1 
		*/

		$yt_regex = '$(?:\n)(?:https?:\/\/)?(?:www\.)?youtu(?:\.be\/|be\.com\/\S*(?:watch|embed)(?:(?:(?=\/[-a-zA-Z0-9_]{11,}(?!\S))\/)|(?:\S*v=|v\/)))([-a-zA-Z0-9_]{11,})(?:[^\r\n]*)$i';
		$yt_var = '$1';

		/* ?feature=oembed&autoplay=1&mute=1&modestbranding=1&playsinline=1&rel=0'; */ 
		$embed_url = 'https://www.youtube-nocookie.com/embed/'.$yt_var.'?feature=oembed&playsinline=1&rel=0';
		$thumb_url = 'https://img.youtube.com/vi/'.$yt_var.'/hqdefault.jpg';
		$yt_svg = '<svg xmlns="http://www.w3.org/2000/svg" style="pointer-events:none;display:block;width:100%;height:100%" viewBox="0 0 28.6 20"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.6 20"><path fill="red" d="M28 3.1A3.6 3.6 0 0 0 25.4.6C23.2 0 14.3 0 14.3 0S5.3 0 3 .6A3.6 3.6 0 0 0 .6 3.1 37.2 37.2 0 0 0 .6 17a3.6 3.6 0 0 0 2.5 2.5c2.3.6 11.2.6 11.2.6s9 0 11.1-.6a3.6 3.6 0 0 0 2.6-2.5c.6-2.3.6-6.9.6-6.9s0-4.6-.6-6.9Z"/><path fill="#fff" d="m11.4 14.3 7.4-4.3-7.4-4.3v8.6Z"/></svg></svg>';
		$yt_srcdoc = htmlentities('<style>* { overflow: hidden; padding: 0; margin: 0;}html,body { height: 100%;}a { position: absolute; z-index: 9999; inset: 0;}a img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center;}span { position: absolute; z-index: 1; inset: 50%; width:84px; height:84px; max-width:20%; transform: translate(-50%, -50%);}span svg {display: block;}</style><a href="'.$embed_url.'"><span>'.$yt_svg.'</span><img loading="lazy" src="'.$thumb_url.'"></a>');
		$yt_replace = '<iframe loading="lazy" srcdoc="'.$yt_srcdoc.'" title="video" width="1200" height="900" src="'.htmlentities($embed_url).'" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="allowfullscreen"></iframe>';

		if(!empty($yt_wrap)){
			$yt_replace = str_replace('{content}', $yt_replace, $yt_wrap);
		} 

		// convert yt to lazy iframe embeds
		$content = trim(preg_replace($yt_regex, $yt_replace, "\n".$content . " ")) . ' <!-- [veppa_yt_lazy_embed:'.round((microtime(true)-$startpreg)/1000).'ms] -->'; 
	}

	return $content;
}

/* Create internal oembed provider. not cached. no theme style */
add_action( 'init', 'veppa_yt_register_oembed_provider' );

function veppa_yt_register_oembed_provider() {
	wp_embed_register_handler(
	'veppa_yt',
	'$(?:https?:\/\/)?(?:www\.)?youtu(?:\.be\/|be\.com\/\S*(?:watch|embed)(?:(?:(?=\/[-a-zA-Z0-9_]{11,}(?!\S))\/)|(?:\S*v=|v\/)))([-a-zA-Z0-9_]{11,})$i',
	'veppa_yt_callback_oembed_provider'
	);
}

function veppa_yt_callback_oembed_provider( $matches ) {
	do_action('qm/debug', 'veppa_yt_callback_oembed_provider');
	return veppa_yt_lazy_embed($matches[0]).'<!-- [veppa_yt_callback_oembed_provider] -->';
}

/* alter urls inside pre so they do not get converted */
add_filter( 'the_content', 'veppa_yt_preserve_pre', 5); 
add_filter( 'the_content', 'veppa_yt_preserve_pre_clean', 99999);

function veppa_yt_preserve_pre($content){
	/* modify youtube url inside pre content so it will not be embedded */
	$pre_regex = '/<pre[^>]*>(.*?)<\/pre>/s';
	$arr_pre = array(); 
	$placeholder = '<!-- preserve this youtube url -->';
	$arr_replace = array(
		'youtube.com' => $placeholder.'you'.$placeholder.'tub'.$placeholder.'e.c'.$placeholder.'om',
		'youtu.be' => $placeholder.'you'.$placeholder.'tu.b'.$placeholder.'e'
	);

	if (strpos($content,'<pre') !== false && preg_match_all($pre_regex, $content, $matches)) {
		foreach ($matches[0] as $index => $pre) { 
			$pre_prevented = str_replace(array_keys($arr_replace), array_values($arr_replace), $pre); 
			$arr_pre[$pre_prevented] = $pre;
		}
		$content = str_replace(array_values($arr_pre), array_keys($arr_pre), $content);
	}

	return $content;
}

function veppa_yt_preserve_pre_clean($content){
	$placeholder = '<!-- preserve this youtube url -->';
	if (strpos($content, $placeholder) !== false) {
		$content = str_replace($placeholder, '', $content);
	}
	return $content;
}

Tested in following environments

These are exactly where “lazy YouTube load” code snippet tested.

  • Classic editor: video URL on its own line or inside “embed” short codes.
  • Block editor: video URL on its own paragraph. In “Video embed” block styling may not work correctly for some themes. Need to manually tweak according to your theme.
  • Plain text widget in “Classic Widgets” and block widgets.
  • Embed block in block widgets.
  • Classic themes: Neve, Sparkling.
  • Block themes: Twentytwentyfive, Twentytwentyfour, Twentytwentythree.

Apart from these it should work in most Classic and FSE themes without problem when video is inserted with URL on its own line or paragraph.

PageSpeed test results

PageSpeed report before and after using Code Snippet to lazy load YouTube videos in WordPress.

Screenshot shows PageSpeed score and metrics “before” and “after” using current code snippet for lazy loading YouTube videos in WordPress website.

Both tests are done with Page cache and lazy loading JavaScript. Only not optimized part is video embeds.

Here is comparison table for WordPress page with one and ten YouTube videos.

PageSpeed score Size Kb Size Diff
1 video 45-90 1315
Lazy 1 video 100 145
10 videos 57 12338 44×
Lazy 10 videos 100 278

It is clear that with lazy load page size decreases dramatically. You save use bandwidth and increase page load speed for your website.

Even with single YouTube video on page you get 9x smaller page. Which is very important for users on mobile device.

With more videos you get bigger difference in page size. On average every YouTube video will add additionally 1.1 MB data before even clicking play button.

Change Log

Version 1.0 (24.07.2025)

  • Initial release

Credits

Lazy load YouTube video iframe inspired by Lazy Embed plugin in 2024.

FAQ

How do I know if this code snippet works?

When this code snippet works you will see YouTube embed as an image first. When you click on image it will load actual YouTube embed code. That will be fully functional YouTube embed. You can click play button or interact using other elements inside that actual YouTube embed.

Check video overview to see how it looks.

Which cache should I clear in order to see this code snippet working?

If you are using page cache you need to clear it. Page cache can be in a Plugin, Server or CDN (like Cloudflare). Page cache will have old YouTube embed codes.

You do not need to clear “oEmebd cache” because this code snippet bypasses it.

Do I need to clear Page cache to see Lazy YouTube Embed code snippet in effect?

Yes, please clear page cache.

As an admin you will always see changes without page cache. For not logged in users to see changes you need to clear page cache.

Do I need to clear oEmbed cache when I start using this plugin?

No, you do not have to clear oEmbed cache. Because this code snippet does not use oEmbed cache. Code snippet populates before WordPress requests built in “oEmbed Cache”.

How to clear oEmbed cache in WordPress?

Clear oEmbed cache in nnWordPress using plugin called WP-Sweep.

Clear oEmbed cache using plugin called “Wp-Sweep”.

  1. Navigate to “Tools” → “Sweep” page.
  2. It will show number of cached items in “oEmbed Caches in Post Meta” row. Click “Sweep” button in that row.

Also you do not need to clear oEmbed cache, because this code snippet bypasses oEmbed cache.

Will lazy loading YouTube videos increase my PageSpeed score to 100?

If everything else like caching, lazy loading JavaScript etc. are already applied and you have videos on your WordPress website then lazy loading YouTube videos will help you to get 100 in PageSpeed score.

Check how I get 100 PageSpeed score for my WordPress website that contains YouTube videos.

What is next:

Leave a Reply

Your email address will not be published. Required fields are marked *