Implementing Lazy Loading for Faster Page Speed
Nadia Gastrom | | 4 min read

Introduction
Lazy loading means deferring offscreen assets until they’re close to the viewport. Done right, it cuts initial bytes and speeds up first render—often improving Core Web Vitals like LCP, CLS, and INP (the measurement framework that maps to real-user speed).[1]
Done wrong, it can hurt LCP (by delaying the hero/LCP image) or add CLS (by letting media pop in without reserved space). The safest path is simple: implement native lazy loading for images/iframes first, lock in dimensions and placeholders, then use JavaScript only where native can’t help. Measure before/after so you can roll back the changes that regress LCP.
What to lazy load (and what not to): quick decision rules
These rules avoid the regressions I see most often when I run page speed audits.
Lazy load these:
- Below-the-fold images (article images after the first screen, carousels below the hero)
- Non-critical iframes (maps, social embeds, videos that aren’t the main content)
- Offscreen galleries (thumbnails/slides not visible yet)
*Do not lazy load these:*
- Above-the-fold hero image (often your LCP candidate) — keep eager and consider
fetchpriority="high" - Logo or layout-critical images that affect initial layout
- Critical CSS and essential fonts needed for first render
Rule of thumb: if it’s likely to appear in the first viewport, load it eager/priority. If LCP gets worse after a rollout, remove lazy loading from the LCP element first and retest.
Implement native lazy loading for images and iframes (the default best practice)
Native lazy loading is the default I ship first: minimal code, good browser support, easy to apply template-by-template.
Images:
- Add
loading="lazy"to images that start offscreen. - Keep above-the-fold images eager (
loading="eager"or omit it). - Set dimensions (
width/height) or use CSSaspect-ratioto prevent layout shifts. - Keep real URLs in
src/srcset(avoid JS-only attributes that can hide URLs from crawlers). srcset/sizesstill work withloading="lazy".
<!-- Hero / likely LCP: eager + prioritized + dimensions -->
<img
src="/images/hero-1280.jpg"
srcset="/images/hero-640.jpg 640w, /images/hero-1280.jpg 1280w"
sizes="(max-width: 768px) 100vw, 1280px"
width="1280"
height="720"
loading="eager"
fetchpriority="high"
alt="Featured product in use"
>
<!-- Below the fold: lazy + dimensions -->
<img
src="/images/article-step-1.jpg"
width="1200"
height="800"
loading="lazy"
decoding="async"
alt="Screenshot of settings panel"
>
decoding="async" can help browsers schedule decode work; treat it as a small win.
Iframes: use lazy loading for embeds users don’t need immediately.
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
width="560"
height="315"
loading="lazy"
title="Demo video"
allowfullscreen
></iframe>
Keep an iframe eager when it’s the page’s main content (for example, a hero video above the fold).
Avoid CLS and broken UX: placeholders, dimensions, and responsive media
CLS happens when a lazy-loaded asset appears and pushes content around because the browser didn’t know what space to reserve.
Reserve space (non-negotiable):
- Set
widthandheightonimg/iframe(the most reliable option). - Or use CSS
aspect-ratioon the media/wrapper so layout stays stable while the request finishes.
Use lightweight placeholders:
- Solid background color
- Small blurred preview (only if you already generate it)
- Minimal CSS skeleton block
Avoid large base64 placeholders across many images; they bloat HTML and can erase the bytes you saved.
Protect UX: don’t lazy load content users expect immediately (a common miss is the first in-article image, even if it’s slightly below the header). On very long pages/infinite scroll, you may need to load slightly ahead of the viewport (that’s where the JS pattern below helps).
Advanced cases: IntersectionObserver for background images and custom components
Native lazy loading doesn’t cover CSS background images and some custom components. Use IntersectionObserver as progressive enhancement when you need “load when near viewport” or tighter control.
// Lazy-load background images with data-bg
const els = document.querySelectorAll('[data-bg]');
if ('IntersectionObserver' in window) {
const io = new IntersectionObserver((entries, observer) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const el = entry.target;
const url = el.getAttribute('data-bg');
if (url) {
el.style.backgroundImage = `url(${url})`;
el.removeAttribute('data-bg');
}
observer.unobserve(el);
}
}, {
rootMargin: '400px 0px',
threshold: 0.01
});
els.forEach(el => io.observe(el));
} else {
els.forEach(el => {
const url = el.getAttribute('data-bg');
if (url) el.style.backgroundImage = `url(${url})`;
});
}
Use rootMargin (roughly 200–600px) to start loading before the element enters view, then unobserve() to keep overhead down. Don’t put your LCP element behind the observer. If you have non-visual work, requestIdleCallback can defer it, but keep it optional.
Conclusion
Implement lazy loading in this order: (1) mark below-the-fold images and non-critical iframes as loading="lazy", (2) keep the likely LCP element eager (optionally fetchpriority="high"), (3) prevent CLS with dimensions or aspect-ratio plus lightweight placeholders, then (4) use IntersectionObserver only for background images and custom components.
Validate with Lighthouse or PageSpeed Insights and watch LCP/CLS. If LCP worsens, remove lazy loading from the LCP candidate first and retest; that’s still the most common failure mode.
Sources
Article author
Nadia Gastrom
Nadia Gastrom is an independent SEO consultant and writer with more than three years of experience helping businesses improve their organic search visibility through SEO strategy, content optimization, and technical SEO. She has worked extensively with SEO platforms such as Semrush and Ahrefs and has a particular interest in how search is evolving beyond traditional rankings. Nadia is currently exploring Answer Engine Optimization (AEO), AI-powered search, and the ways businesses can make their content more useful and discoverable across emerging search experiences. When she is not researching search trends or writing about SEO, Nadia enjoys travelling, discovering new places, and spending time with dogs. She continues to follow the SEO and AEO industry closely to understand what is changing and what marketers should be preparing for next.

