Every byte a browser downloads before it needs to costs the user time. On a page with 40 images, a video embed, and three iframes, the browser will - by default - fetch all of them simultaneously, regardless of whether the user can actually see any of them. Lazy loading changes that contract: resources are fetched only when they are about to enter the viewport, dramatically reducing initial page weight and improving load times for the content that actually matters first.
This guide covers the full picture - native browser support, JavaScript-based approaches, WordPress-specific implementation, the facade pattern for video embeds, and the mistakes that turn a performance win into an SEO or UX problem.
What Lazy Loading Is and Why It Matters
Lazy loading is a loading strategy that defers the download of offscreen resources until the user scrolls close enough to need them. The alternative - loading everything upfront - is called eager loading, and it is the browser's default behavior.
The performance case is straightforward. A long-form article page might have 20 images below the fold. Without lazy loading, all 20 are requested on page load, competing for bandwidth with the above-the-fold content the user sees immediately. With lazy loading, only the first two or three visible images load initially. The rest are fetched as the user scrolls, spreading the network load across the session rather than front-loading it.
The measurable impact shows up directly in Core Web Vitals. Reducing the number of render-blocking and bandwidth-consuming requests at page load improves Largest Contentful Paint (LCP) and Total Blocking Time (TBT). It also reduces data transfer costs for users on metered mobile connections - a factor that matters more than most desktop-focused developers assume.
Native Lazy Loading with the loading Attribute
The simplest implementation requires no JavaScript at all. The HTML loading attribute, now supported across all modern browsers, tells the browser how to prioritize a resource's fetch.
<img
src="product-photo.jpg"
alt="Leather wallet in dark brown"
width="800"
height="600"
loading="lazy"
>
<iframe
src="https://www.google.com/maps/embed?pb=..."
width="600"
height="450"
loading="lazy"
title="Office location map"
></iframe>The attribute accepts three values: lazy defers loading until the element is near the viewport, eager loads immediately regardless of position (equivalent to the default), and auto leaves the decision to the browser - though browser implementations of auto vary and it is rarely preferable to being explicit.
Browser support is now broad. Chrome has supported loading="lazy" since version 76 (released 2019), Firefox since version 75, Safari since version 15.4. Edge supports it via the same Chromium engine as Chrome. Global support sits above 95% as of 2025, meaning the attribute is safe to use without a polyfill for the vast majority of audiences.
For the small percentage of users on unsupported browsers, the attribute is simply ignored - images load eagerly as they always did. This graceful degradation makes native lazy loading extremely low-risk to adopt.
How the Browser Determines the Threshold
Browsers do not wait until an image is literally at the viewport edge to start downloading it. They apply a distance threshold - typically several hundred pixels below the visible area - so the image is already loaded by the time the user scrolls to it. The exact threshold varies by browser and connection speed: Chrome uses a larger threshold on slow connections to compensate for download time. Developers do not control this threshold directly, which is actually a feature - the browser makes the right call based on real network conditions.
JavaScript-Based Lazy Loading: When You Need It
Native lazy loading covers the majority of use cases, but JavaScript-based solutions remain relevant in specific scenarios:
Background images set via CSS - the
loadingattribute only applies to<img>and<iframe>elements. Adivwith a CSSbackground-imagehas no native mechanism for lazy loading. JavaScript using the Intersection Observer API can swap in the real image URL once the element enters the viewport.Complex component hydration - single-page applications or heavily componentized themes may need to defer not just images but entire JavaScript-driven components until they are needed. A JavaScript lazy loading strategy tied to the Intersection Observer gives precise control over that lifecycle.
Older browser support requirements - if your analytics show meaningful traffic from Safari versions below 15.4 or other legacy browsers, a JavaScript polyfill ensures consistent behavior.
The dominant library for JavaScript lazy loading is vanilla-lazyload by Andrea Verlok, which weighs around 3KB minified and gzipped, uses the Intersection Observer API natively, and falls back gracefully. For most WordPress projects, native lazy loading has made this library unnecessary - but it remains the correct choice when CSS background images or legacy browser parity are requirements.
Lazy Loading in WordPress
WordPress added native lazy loading support in version 5.5 (released August 2020). The core behavior automatically adds loading="lazy" to all images inserted through the block editor, post content, post thumbnails, avatars, and images inserted via wp_get_attachment_image(). For most WordPress sites, this means lazy loading is already working without any plugin or configuration.
WordPress 6.3 extended this behavior to iframes embedded in post content, adding loading="lazy" automatically to iframe tags where absent.
What WordPress Does Not Handle Automatically
The automatic behavior has boundaries worth knowing. Images output directly in theme templates using hardcoded <img> tags without going through WordPress image functions will not receive the attribute unless the theme developer adds it. Images loaded via JavaScript (carousels, sliders, dynamic galleries) are outside WordPress's scope entirely. Third-party page builders that render images through their own pipelines may or may not respect WordPress's filters.
WordPress also applies a deliberate exception: the first image in post content is given loading="eager" rather than loading="lazy", because that image is often the LCP element. This is the correct behavior - more on why in the mistakes section below.
Theme and Plugin Considerations
If you are building or auditing a WordPress theme, verify that images output in header.php, functions.php, or custom template parts use wp_get_attachment_image() or equivalent functions rather than raw <img> tags. The WordPress function applies lazy loading, width and height attributes, and responsive srcset values in one call - doing this manually is error-prone and rarely more flexible.
For sites with significant image libraries or complex media workflows, running a full audit with a tool like the SEO Analyzer surfaces missing attributes, oversized images, and missing alt text across all page types - not just the homepage.
Videos and Iframes: The Facade Pattern
A YouTube embed iframe does not just load a placeholder when the page loads - it requests multiple JavaScript files, stylesheets, and tracking pixels from Google's servers the moment the iframe is parsed. A single YouTube embed can add 500KB or more to initial page weight and trigger dozens of additional HTTP requests, even if the user never clicks play.
The facade pattern solves this by replacing the real embed with a lightweight static image that looks like the video player. The actual iframe is only injected into the DOM when the user clicks the play button.
<!-- Facade pattern for a YouTube embed -->
<div class="video-facade" data-video-id="dQw4w9WgXcQ" style="position:relative; cursor:pointer;">
<img
src="https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg"
alt="Video title here"
width="480"
height="360"
loading="lazy"
>
<button aria-label="Play video" class="facade-play-btn">▶</button>
</div>
<script>
document.querySelectorAll('.video-facade').forEach(function(facade) {
facade.addEventListener('click', function() {
var videoId = facade.dataset.videoId;
var iframe = document.createElement('iframe');
iframe.src = 'https://www.youtube-nocookie.com/embed/' + videoId + '?autoplay=1';
iframe.width = '480';
iframe.height = '360';
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
iframe.title = facade.querySelector('img').alt;
facade.replaceWith(iframe);
});
});
</script>Note the use of youtube-nocookie.com rather than youtube.com - this reduces the tracking cookies YouTube sets on your visitors before they have interacted with the embed.
The same pattern applies to Vimeo and other embed providers. The thumbnail URL format differs, but the principle is identical: static image first, real embed on demand. For WordPress specifically, the lite-youtube-embed web component by Paul Irish implements a polished, accessible version of this pattern as a drop-in custom element.
Common Mistakes That Hurt Performance and SEO
Lazy Loading Above-the-Fold Images
Applying loading="lazy" to images visible in the initial viewport is one of the most counterproductive mistakes in performance optimization. When the browser encounters a lazy image, it deprioritizes it - even if that image is the LCP element. The result is a measurably slower LCP score, which directly affects both user experience and Google's ranking signals.
The rule is straightforward: any image that is visible without scrolling on any common viewport size should use loading="eager" or omit the loading attribute entirely. Hero images, above-the-fold product photos, and logo images should never be lazy loaded. If you are unsure which images are above the fold across device sizes, test at 375px wide (a common mobile width) as well as desktop.
Missing Width and Height Attributes
Without explicit width and height attributes on <img> elements, the browser cannot reserve space for the image before it loads. As lazy images load during scroll, they push content down - a phenomenon measured by Cumulative Layout Shift (CLS). CLS is a Core Web Vitals metric, and poor CLS scores affect both user experience and search rankings.
<!-- Wrong: no dimensions, will cause layout shift -->
<img src="photo.jpg" alt="Team photo" loading="lazy">
<!-- Correct: dimensions allow the browser to reserve space -->
<img src="photo.jpg" alt="Team photo" width="1200" height="800" loading="lazy">The width and height attributes in HTML do not force the image to render at those exact pixel dimensions - CSS controls the display size. Their purpose is to communicate the aspect ratio to the browser so it can allocate the correct amount of vertical space before the image file arrives.
SEO Considerations: Does Google Index Lazy Loaded Images?
The short answer is yes - Googlebot crawls and indexes lazy loaded images, including those loaded via JavaScript. Google has confirmed that its crawler executes JavaScript and can trigger Intersection Observer callbacks, meaning modern JavaScript-based lazy loading is compatible with indexing.
However, there are nuances. Googlebot crawls pages in waves: an initial crawl renders the HTML, and a secondary crawl (which may happen hours or days later) executes JavaScript. Images that only appear after JavaScript execution may take longer to be indexed than images present in the initial HTML. For critical images - product photos, article hero images, images you want indexed promptly - being present in the HTML source (even as a src attribute on a native lazy image) is preferable to being injected entirely by JavaScript.
Native lazy loading with the loading attribute keeps the src in the HTML from the start. The browser defers the download, but the URL is immediately visible to crawlers that read HTML without executing JavaScript. This makes native lazy loading the most search-engine-friendly approach.
For image SEO more broadly - descriptive filenames, alt text, structured data for product images - the lazy loading mechanism itself is neutral. What matters is that the image URL and its surrounding context are accessible to the crawler, which native lazy loading fully satisfies. Pairing lazy loading with a thorough technical audit, including checking how images appear to crawlers, is worth doing periodically. The free SEO Analyzer covers image-related signals alongside broader on-page and technical factors in a single pass.
Putting It Together
Lazy loading is not a single technique - it is a set of decisions that need to match the resource type, the position on the page, and the rendering environment. Native loading="lazy" handles the majority of image and iframe cases with zero JavaScript overhead and broad browser support. The facade pattern is the correct tool for third-party video embeds, where the real cost is not the video file but the third-party scripts that load with the iframe. JavaScript-based approaches remain valuable for CSS background images and complex component scenarios.
The mistakes - lazy loading LCP images and omitting dimensions - are avoidable with a straightforward rule: audit which images are above the fold, always declare dimensions, and treat the first visible image as a performance asset to be prioritized rather than deferred. Getting these details right is what separates a page that scores well in lab tests from one that actually feels fast to real users on real connections.