WordPress 5.5 added automatic loading="lazy" attribute injection for images and iframes inserted via the_content(), get_the_post_thumbnail(), and wp_get_attachment_image() — this native browser lazy loading defers off-screen image downloads until the user scrolls near them, reducing initial page weight by 30–60% on image-heavy pages without any JavaScript. The native attribute works in all modern browsers (Chrome 77+, Firefox 75+, Edge 79+, Safari 15.4+) with no polyfill needed, but requires that the image has explicit width and height attributes so the browser can calculate the layout box before the image loads and reserve space to prevent cumulative layout shift (CLS). WordPress automatically adds width and height to images output through the template functions listed above — custom <img> tags hardcoded in templates must have these attributes added manually. The above-the-fold hero image — typically the post featured image or the first image in a post — should have loading="eager" and optionally fetchpriority="high" to signal to the browser that it is the Largest Contentful Paint (LCP) element and should be loaded as soon as possible; WordPress adds loading="lazy" to all images by default, including the first one, which delays LCP and hurts Core Web Vitals. The wp_lazy_loading_enabled filter controls which contexts get the attribute: returning false from it for the first image in a post or for the_post_thumbnail context on single posts fixes the LCP issue. Embedded YouTube and Vimeo iframes benefit from loading="lazy" in the same way — the embed_oembed_html filter can inject the attribute into oEmbed output. For browsers that do not support loading="lazy" (primarily Safari before 15.4), an IntersectionObserver polyfill using data-src + JavaScript swap provides equivalent behavior; detect support with 'loading' in HTMLImageElement.prototype. The Redis object caching post addresses server-side performance while lazy loading addresses client-side load time — both are needed for strong Core Web Vitals scores.
Problem: A WordPress photography site loads 40+ full-resolution images on every page, resulting in 8–12 MB initial page weight and a 6–8 second LCP score — even though only the first 3–4 images are visible above the fold.
Solution: Confirm that WordPress is injecting loading="lazy" on below-the-fold images, override it to loading="eager" with fetchpriority="high" on the hero featured image to fix LCP, and ensure all images have explicit width and height attributes to eliminate CLS.
// 1. Remove lazy loading from the featured image on single posts (it is the LCP element)
add_filter('wp_lazy_loading_enabled', function(bool $default, string $tag_name, string $context): bool {
if ($tag_name === 'img' && $context === 'the_post_thumbnail' && is_single()) {
return false; // no loading="lazy" on the hero featured image
}
return $default;
}, 10, 3);
// 2. Add fetchpriority="high" to the featured image on single posts
add_filter('wp_get_attachment_image_attributes', function(array $attr, WP_Post $attachment, $size): array {
if (is_single() && get_post_thumbnail_id() === $attachment->ID) {
$attr['fetchpriority'] = 'high';
$attr['loading'] = 'eager';
}
return $attr;
}, 10, 3);
// 3. Add loading="lazy" to YouTube/Vimeo iframes in oEmbed output
add_filter('embed_oembed_html', function(string $html): string {
return str_replace('
// IntersectionObserver polyfill for loading="lazy" in older Safari
// Only activates if native lazy loading is not supported
(function () {
if ('loading' in HTMLImageElement.prototype) return; // native support, skip
const images = document.querySelectorAll('img[data-src]');
if (!images.length) return;
const observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
const img = entry.target;
img.src = img.dataset.src;
if (img.dataset.srcset) img.srcset = img.dataset.srcset;
img.removeAttribute('data-src');
img.removeAttribute('data-srcset');
observer.unobserve(img);
});
}, { rootMargin: '300px' });
images.forEach(function (img) { observer.observe(img); });
}());
NOTE: The loading="lazy" attribute only defers loading — it does not resize or compress images. Combine it with proper image sizing (the add_image_size() sizes registered in functions.php), next-gen formats (webp output via a plugin or server-level conversion), and responsive srcset attributes (added automatically by wp_get_attachment_image()) for maximum impact on page weight and Core Web Vitals scores.