Fixing Slow LCP on Pages With Embedded Video

Video makes landing pages and tutorials better and makes LCP worse, in ways that depend on how the video is embedded. A self-hosted hero video's LCP is usually its poster image, which browsers discover late and often load at low priority. A YouTube or Vimeo iframe is not an LCP candidate at all, but it drags in a megabyte of player code and several third-party connections that compete with whatever is the LCP element. And an autoplaying background video downloads several megabytes during the load, on every visit, often on mobile data.

This guide fixes LCP on two kinds of page on a product marketing site built with Astro: a landing page with a self-hosted hero video, and 140 tutorial pages with YouTube embeds near the top. It belongs to Largest Contentful Paint Optimization for Static Sites.

Symptoms

PageLCP p75 mobileLCP elementPage weight
Landing (hero video)3.4 svideo poster6.8 MB (video autoplay)
Tutorials (YouTube embed)2.9 sh1 or first paragraph1.6 MB

On the landing page the poster was slow. On tutorials the LCP element was text, which should have been fast — but the YouTube iframe's resources were competing with the CSS and web font that the text needed.

Fix 1: Make the Poster Fast

The hero used <video autoplay muted loop playsinline poster="/hero-poster.jpg">. The poster was a 380 KB JPEG at 2560 pixels wide, discovered only when the parser reached the video element, and fetched at default (low-ish) priority. Three changes:

  • Encode the poster properly. AVIF and WebP at the widths actually displayed, generated by astro:assets. The mobile poster became 34 KB.
  • Make it an image the browser prioritises. The visual poster is now an <img> with fetchpriority="high" behind the video, and the video has no poster attribute; the video fades in over it once it can play. The image is the LCP element and loads first.
  • Preload it in the head for the hero breakpoint, as in Reducing LCP from Hero Images on Static Sites.
<div class="hero-media">
  <Picture src={poster} widths={[480, 960, 1600]} sizes="100vw" formats={['avif', 'webp']}
           alt="" fetchpriority="high" loading="eager" class="hero-poster" />
  <video class="hero-video" muted loop playsinline preload="none" data-src="/media/hero-720.mp4"></video>
</div>
Poster image size and load start time before and after the fix Paired bars. Poster bytes on mobile went from 380 kilobytes to 34 kilobytes. The time the poster request started went from 1,150 milliseconds to 160 milliseconds. LCP in the lab went from 3,600 to 1,500 milliseconds. The hero poster before and after Before After Poster bytes (KB) 380 34 Request starts (ms) 1150 160 Lab LCP (ms) 3600 1500
WebPageTest, Moto G Power, Slow 4G, median of nine runs.

Fix 2: Load the Video After LCP, Lighter

The hero video autoplayed a 6.1 MB 1080p MP4 on every visit, including phones. It now:

  • Has preload="none" and no src until after the page has loaded and the browser is idle; a small script sets the source then.
  • Picks a source by viewport width: a 720p, 1.2 MB AV1/H.264 pair for desktops, and on phones nothing at all — the poster image stays.
  • Respects prefers-reduced-motion by never loading the video for those readers.
  • Respects the Save-Data header and navigator.connection.saveData.
const v = document.querySelector('.hero-video');
const ok = matchMedia('(min-width: 48em)').matches && !matchMedia('(prefers-reduced-motion: reduce)').matches
  && !navigator.connection?.saveData;
if (ok) requestIdleCallback(() => { v.src = v.dataset.src; v.play().then(() => v.classList.add('playing')); });

The video fades in over the poster with an opacity transition, which does not count as layout shift.

Fix 3: Facades for YouTube Embeds

Each tutorial embedded a YouTube video near the top with the standard iframe. Before the reader did anything, the page loaded the player's JavaScript, its CSS, several thumbnails and connections to four Google domains — 1.1 MB on mobile. That competed with the page's own CSS and font, delaying the text that was the LCP element.

The embeds were replaced with a facade: a static thumbnail and a play button, rendered at build time, which load the real player only when clicked. The site uses lite-youtube-embed, with the thumbnail self-hosted and optimised at build time rather than fetched from YouTube:

<lite-youtube videoid={id} playlabel={`Play: ${title}`}
  style={`background-image: url(${thumb.src})`}></lite-youtube>

On click, it loads the iframe with autoplay=1 so one click plays the video. Lazy-Loading YouTube Embeds on Static Sites covers the facade in detail; the same approach works for Vimeo with lite-vimeo.

Resources loaded by a tutorial page with a standard embed and with a facade Two panels. With the standard YouTube iframe, the page loaded about 1.1 megabytes including the player JavaScript, CSS and thumbnails, and opened connections to four Google domains before any interaction. With the facade, it loaded one self-hosted 22 kilobyte thumbnail and a 3 kilobyte script, with no third-party connections until the reader clicked. What a tutorial page loads before the reader clicks play Standard iframe ~1.1 MB before play 4 third-party domains player JS competes with CSS LCP 2.9 s Facade 22 KB thumbnail + 3 KB script 0 third-party connections player loads on click LCP 1.7 s
DevTools network panel on mobile, before any interaction.

Fix 4: Reserve Space and Keep Captions

Both the hero and the facades reserve their space with aspect-ratio: 16 / 9, so nothing shifts when the player or video arrives. And the facade keeps accessibility intact: the play button has an accessible name including the video title, and each tutorial links to a transcript, which also gives search engines the video's content as text.

Decision tree for embedding video without hurting LCP The root asks what the video is for. A decorative background video should be replaced by an image on mobile and loaded after idle on desktop. A hero video carrying information should use a prioritised poster image and a compressed source loaded after LCP. A third-party tutorial video should use a facade that loads the player on click. A short silent loop can become an animated AVIF or WebP image. Choosing how to embed video What is the video for? Decorative background Image on mobile video after idle on desktop Hero with information Fast poster video after LCP YouTube / Vimeo tutorial Facade player on click Short silent loop Animated AVIF no player at all
In every branch, the first paint shows an optimised still image.

Self-Hosting Video Files

The hero video files were moved from the site's own static output to a video-specific path served with byte-range support and long caching, because static hosts differ in how well they handle range requests for large media. Encoding used two renditions — AV1 at 1.2 MB for browsers that support it and H.264 at 1.9 MB as fallback — listed as <source> elements in order, at 720p and a low bitrate suitable for a muted background loop. Encoding the loop at 24 frames per second instead of 60 halved the file size with no visible difference for this content. For longer product videos, the site uses a video platform with adaptive streaming rather than self-hosting, embedded through the same facade pattern so the player still loads only on click.

Testing Video Pages

A Lighthouse CI run on the landing page and three tutorials asserts that the LCP element is an image or text, never a video or iframe, and that no request to youtube.com or googlevideo.com happens before interaction. A Playwright test clicks a facade and checks that the player loads and starts playing within three seconds, so the facade cannot silently break playback.

Measured Impact

MeasureBeforeAfterTool
Landing LCP p75, mobile3.4 s1.6 sweb-vitals RUM
Landing page weight, mobile6.8 MB0.4 MBDevTools
Tutorial LCP p75, mobile2.9 s1.7 sweb-vitals RUM
Tutorial weight before play1.6 MB0.5 MBDevTools
Video plays per tutorial view31%30%YouTube Analytics
CLS from video areas0.040web-vitals attribution

Video plays per view barely changed: readers who wanted to watch clicked the facade just as they had clicked the player.

Pitfalls & Rollback

  • Relying on the poster attribute for the LCP image. It is discovered late; use a prioritised image.
  • Autoplaying large video on phones. Show the poster; load video only where it adds value.
  • Standard iframes for third-party video. Use facades; the player loads on click.
  • Forgetting reduced motion and Save-Data. Respect both.
  • Rollback: the facade and the hero component are independent; either can return to the previous markup without affecting the other.

Conclusion

Video slows LCP through a late, heavy poster, a large autoplaying file loaded during the critical path, and third-party players that compete with the page's own resources. A prioritised, optimised poster image, video loaded only after LCP and only where it helps, and facades for YouTube embeds took the landing page from 3.4 to 1.6 seconds and tutorials from 2.9 to 1.7 seconds on mobile, without reducing how often readers watched.

FAQ

Can a video be the LCP element?

The video element itself counts through its poster image, and in current Chrome versions through the first painted frame of the video. In practice, a hero video's LCP is usually its poster image, so a large, unoptimised or late-discovered poster delays LCP. An iframe embed such as YouTube is not an LCP candidate, but the thumbnail inside it is not either, so the LCP element becomes whatever else is large.

Should a hero video autoplay?

Autoplaying muted video is allowed by browsers but costs bandwidth and main-thread time on every visit. If it carries real information, keep it but deliver a small, compressed file with a fast poster. If it is decorative, a static image or a short animated AVIF is lighter, and readers who prefer reduced motion should get the still image.

How much does a YouTube embed slow a page?

A standard YouTube iframe loads around 1 megabyte of JavaScript and other resources and several third-party connections before the reader presses play. On the pages in this guide, replacing embeds with a facade that loads the player on click cut total page weight by 1.1 megabytes per embed and improved LCP by about 0.8 seconds on mobile.

Should the poster image be preloaded?

If the poster is the LCP element, yes, with fetchpriority high, or by making it discoverable early in the HTML. The browser discovers a video's poster only when it parses the video element, and may give it low priority. A preload or an img placed in the markup as the visual poster ensures it loads first.

Filed underFixes