Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Optimize and compress web videos

Videos are compressed, resized per device, and served in efficient codecs (H.264, HEVC, VP9/AV1) to reduce page weight and bandwidth use.

Utilities
Quick take
Typical fix time 20 min
  • Re-encode source video instead of shipping raw camera/screen-recorder output
  • Serve multiple resolutions so mobile doesn't download a 4K file
  • Prefer modern codecs: AV1/VP9/HEVC over baseline H.264 where supported
  • Strip audio tracks from silent background/looping videos
Why it matters: Unoptimized video is frequently the single heaviest asset on a page—an uncompressed or oversized video can dwarf every image and script combined, delaying Largest Contentful Paint and burning mobile data budgets.

Rule Details

Video is often the heaviest single asset on a page. A source file straight off a camera or screen recorder is typically encoded for archival quality, not for the size and bitrate a browser actually needs—re-encoding it for the web can cut file size dramatically with no visible difference at normal playback sizes.

Code Example

Serve the smallest codec/resolution combination the browser supports, with a fallback for older browsers.

<video poster="hero-poster.jpg" preload="metadata" muted playsinline controls>
  <source src="hero-1080p.av1.webm" type="video/webm; codecs=av01.0.05M.08" />
  <source src="hero-1080p.vp9.webm" type="video/webm; codecs=vp9" />
  <source src="hero-1080p.h264.mp4" type="video/mp4" />
  Your browser does not support the video tag.
</video>
# FFmpeg: re-encode to H.264 at a reasonable quality/size tradeoff
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset slow -c:a aac -b:a 128k output.mp4
 
# FFmpeg: VP9/WebM, smaller than H.264 at the same visual quality
ffmpeg -i input.mov -c:v libvpx-vp9 -crf 32 -b:v 0 -an output.webm
 
# Strip audio entirely from a silent background/looping video
ffmpeg -i input.mp4 -c copy -an output-no-audio.mp4

Why It Matters

Unoptimized video is frequently the single heaviest asset on a page—an uncompressed or oversized video can dwarf every image and script combined, delaying Largest Contentful Paint and burning mobile data budgets.

Resolution and Bitrate

Match the delivered resolution to how large the video will actually render, not the resolution it was captured at.

Player sizeReasonable source resolution
Full-bleed hero (desktop)1080p
Inline article video720p
Thumbnail/preview480p or a static poster image

Serving a 4K background video behind a 400px-tall hero section wastes bandwidth the same way an oversized <img> does.

Codec Choice

CodecContainerCompressionBrowser Support
AV1WebM/MP4BestModern browsers, slow to encode
VP9WebMVery goodBroad modern support
HEVC (H.265)MP4Very goodSafari, licensing constraints elsewhere
H.264MP4Good (baseline)Universal fallback
Good to Know

Provide multiple <source> elements ordered from most to least efficient codec—the browser picks the first one it can play, so list AV1/VP9 before the H.264 fallback.

Framework Examples

Codec fallback via <source> solves format negotiation, but the native <video> element still has no way to pick a resolution per breakpoint the way <picture> does for images with media queries on <source>. A naive fix either ships every resolution in one hidden-by-CSS <video> per size (the browser downloads all of them regardless of display: none) or requires manual JS to swap sources after the fact.

import ReactResponsiveVideo from '@damiarita/react-responsive-video'
 
function ResponsiveHero() {
  return (
    <ReactResponsiveVideo
      videoProps={{ autoPlay: true, muted: true, loop: true, playsInline: true }}
      sizes={[
        {
          width: 1920,
          height: 1080,
          mediaQuery: '(min-width: 1200px)',
          videoSources: [
            { url: '/hero-1080p.webm', format: 'video/webm' },
            { url: '/hero-1080p.mp4', format: 'video/mp4' },
          ],
          posterSources: [
            { url: '/hero-1080p.webp', format: 'image/webp' },
            { url: '/hero-1080p.jpg', format: 'image/jpeg' },
          ],
        },
        {
          width: 640,
          height: 360,
          videoSources: [
            { url: '/hero-360p.webm', format: 'video/webm' },
            { url: '/hero-360p.mp4', format: 'video/mp4' },
          ],
          posterSources: [
            { url: '/hero-360p.webp', format: 'image/webp' },
            { url: '/hero-360p.jpg', format: 'image/jpeg' },
          ],
        },
      ]}
    />
  )
}

This component renders an SSR-safe <picture> placeholder first, then swaps in the <video> for the resolution matching the resolved media query once it's running in the browser—so only one poster/video pair downloads instead of one per breakpoint.

Common Mistakes

Mistakes to Avoid
  • Shipping the raw capture file — Camera and screen-recorder output is rarely encoded for web delivery
  • One resolution for every device — Forces mobile to download the same file as a 4K desktop monitor
  • Keeping audio on muted background video — The audio track still adds bytes even if the browser never plays it
  • preload="auto" on below-the-fold video — Downloads the whole file before the user may ever see it
  • No poster image — The player shows a blank or black box until the first frame decodes
When to Break This Rule
  • Short, silent looping clips (e.g. UI micro-interactions) may be small enough that further compression isn't worth the added encode complexity
  • User-generated or on-demand uploads often need a server-side transcoding pipeline rather than build-time encoding
  • Live streams have different constraints (adaptive bitrate via HLS/DASH) than the static-file guidance above

Verification

Automated Checks

  • Check transferred file size in DevTools' Network tab, filtered to Media
  • Run Lighthouse and review any "Efficiently encode video" or LCP-related flags

Manual Checks

  • Confirm the player-rendered size roughly matches the delivered resolution (no visible downscaling of a much larger source)
  • Play the video on a throttled mobile network profile to confirm reasonable start time

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Scan this codebase for <video> elements and video asset references. For each one, verify: 1) The served resolution is appropriate for the rendered player size and device (no 4K video in a 400px player). 2) The codec/container is efficient (H.264 baseline is acceptable but VP9/AV1/HEVC is preferred when available). 3) Audio is stripped from silent background/looping videos. 4) A poster attribute or lightweight thumbnail is present. 5) File size is reasonable for its duration and placement (hero/background videos under a few MB). Report issues grouped by severity with file paths.

Fix

Auto-fix issues

For each video optimization issue found: 1) Re-encode to multiple resolutions (e.g. 480p/720p/1080p) and serve the smallest one that fits the player via <source> or JS-based selection. 2) Convert to an efficient codec (VP9 or AV1 in WebM, or HEVC/H.264 in MP4) with a reasonable CRF/ quality setting rather than default encoder settings. 3) Remove the audio track from muted background/looping videos. 4) Add a poster attribute pointing to a compressed thumbnail. 5) Add preload="metadata" (or "none") instead of "auto" for below-the-fold videos. Show the corrected <video> markup and encoding command.

Explain

Learn more

Explain why video is often the largest asset on a page and how it affects Largest Contentful Paint and mobile data usage. Cover the tradeoffs between H.264, HEVC, VP9, and AV1 (compression efficiency vs browser/ device support and encode time), how resolution and bitrate should scale to the rendered player size and viewport, and why muted background video should have its audio track stripped entirely rather than just muted in markup.

Review

Code review

Review video assets, <video>/<source> markup, and any encoding or CDN transform pipeline related to Optimize and compress web videos. Flag exact files or components where resolution, codec, bitrate, or loading behavior violates the rule, and describe how to confirm the fix in DevTools' Network and Media panels.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

web.dev: Replace GIFs with video

by web.dev

web.devArticle
HandBrakehandbrake.frTool
FFmpegffmpeg.orgTool
SlingSiteslingsite.github.ioTool

Rules that often go hand-in-hand with this one.

Optimize all images for web

Images are optimized with appropriate formats, compression, and modern techniques.

Images
Convert animated GIFs to video

Large animated GIFs are replaced with more efficient video formats like MP4 or WebM to reduce page weight.

Performance
Implement lazy loading for offscreen content

Images and heavy resources below the fold are lazy loaded to improve initial performance.

Performance
Add thumbnail images to videos

HTML5 video elements should have a poster attribute providing a thumbnail image displayed before the video loads or is played.

HTML

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 386