A full-width hero video looks great in the design file. In production, it's often the heaviest thing on the page, and it quietly drags down your performance score.
This is a checklist I use whenever video shows up in a frontend project. Each item is small, but together they make a big difference.
First, measure
Before changing anything, find out whether video is actually your problem:
- Run Lighthouse or PageSpeed Insights on the page.
- In Chrome DevTools, open the Network tab, filter by Media, and sort by size.
- In the Performance panel, check what element is reported as your LCP. If your LCP element is a video poster or a frame, or if the video request competes with your critical resources on a slow connection, keep reading. The checklist
- Compress the source file first Most videos exported from editors or screen recorders are far heavier than they need to be. A sensible web-friendly baseline with FFmpeg: ffmpeg -i input.mp4 \ -c:v libx264 -crf 26 -preset slow -pix_fmt yuv420p \ -c:a aac -b:a 96k \ -movflags +faststart \ output.mp4
If you'd rather not touch the command line, an online tool such as VideoBee's video compressor does the same job in the browser. (Disclosure: I work on VideoBee.)
- Use the right resolution A background video behind a headline rarely needs 4K. Match the video to how it's displayed: ffmpeg -i input.mp4 -vf "scale=-2:720,fps=30" -c:v libx264 -crf 26 -an hero-720.mp4
For decorative background video, 720p (or even lower) is often enough.
- Drop the audio track when it's not used Muted autoplay backgrounds don't need audio at all. -an removes it and saves bytes.
- Keep it short
A 6 to 10 second loop is usually all a hero needs. If you have a longer source, trim it. A browser-based video cutter is handy for quick trims, or use FFmpeg:
ffmpeg -ss 00:00:03 -to 00:00:11 -i input.mp4 -c:v libx264 -crf 26 -an loop.mp4
- Provide modern formats with a fallback WebM (VP9) and AV1 can be smaller than H.264 at similar quality. Offer them first and keep MP4 as the universal fallback:
Create the WebM version:
ffmpeg -i hero.mp4 -c:v libvpx-vp9 -crf 33 -b:v 0 -an hero.webm
Need to convert between formats without installing anything? A video converter works for one-off jobs.
Browsers pick the first
- Always set a poster image The poster shows immediately while the video loads, so users never see an empty box. Generate one from a frame: ffmpeg -i hero.mp4 -frames:v 1 -q:v 3 hero-poster.jpg
Optimize the poster too (modern formats like WebP or AVIF work), because in many cases it becomes the element that determines your LCP.
- Control preloading The preload attribute tells the browser how aggressively to fetch the video: ● preload="none": don't download anything until the user interacts (good for below-the-fold videos). ● preload="metadata": fetch only duration and dimensions. ● preload="auto": let the browser decide (can be expensive on mobile). For non-critical videos, none or metadata is usually the right call.
- Lazy-load videos below the fold Don't load videos the user hasn't scrolled to. A small IntersectionObserver does the job:
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const video = entry.target;
video.src = video.dataset.src;
video.load();
video.play().catch(() => {});
obs.unobserve(video);
});
});
document.querySelectorAll("video[data-src]").forEach((v) => observer.observe(v));
- Make sure the server supports range requests and caching Browsers fetch video in chunks using HTTP range requests. Check that your server or CDN returns Accept-Ranges: bytesand sets sensible Cache-Control headers for video files. Putting videos behind a CDN also helps users far from your origin.
- Use +faststart for MP4 Moving the MP4 metadata (the moov atom) to the beginning of the file lets playback start before the entire file is downloaded. It's a single flag: -movflags +faststart.
- Consider not autoplaying at all For longer product demos, a poster with a play button (and the video loaded on click) is much lighter than an autoplaying video. Reserve autoplay for short, muted loops.
- Respect user preferences Some users ask their OS to reduce motion or save data. You can honor that: @media (prefers-reduced-motion: reduce) { .hero-video { display: none; } }
Show the poster image instead.
Before and after
Measure the same page before and after applying the checklist, and record:
Metric
Before
After
Video file size
measure
measure
LCP
measure
measure
Total page weight
measure
measure
[Add your own numbers and a Lighthouse screenshot here. Real before/after data is far more convincing than generic claims.]
The short version (save this)
● [ ] Compress the source (CRF 24 to 28 for web)
● [ ] Reduce resolution and frame rate to what the layout needs
● [ ] Remove audio if the video is muted
● [ ] Trim to the shortest useful loop
● [ ] Offer WebM/AV1 plus an MP4 fallback
● [ ] Set an optimized poster
● [ ] Use preload="none" or "metadata" where possible
● [ ] Lazy-load below-the-fold videos
● [ ] Enable range requests, caching, and a CDN
● [ ] Add -movflags +faststart to MP4s
● [ ] Don't autoplay long videos
● [ ] Respect prefers-reduced-motion
What's the worst video-related performance problem you've found in the wild? Tell me in the comments.

Top comments (0)