What is Lazy Loading?
Defers loading non-critical resources until they're needed. Browser fetches images, iframes, JS as the user scrolls. Improves LCP and bandwidth.
What is lazy loading?
Lazy loading is a performance pattern that defers loading non-critical resources until they are actually needed — typically when the resource is about to scroll into the viewport. Instead of fetching every image, iframe, and JavaScript bundle upfront, the browser loads only what is immediately visible and pulls in the rest on demand.
The user-facing benefit is straightforward: pages render faster because the browser has less to download before first paint. The technical benefit is more interesting: lazy loading shifts load off the critical path, which directly improves Largest Contentful Paint (LCP), Total Blocking Time (TBT), and bandwidth costs. Mobile users on slow connections feel this most.
The four types of lazy loading you'll actually use
1. Native image lazy loading (the easy win)
Modern browsers (Chrome 76+, Safari 15.4+, Firefox 75+) support a native attribute that defers off-screen images:
<img src="hero.jpg" loading="lazy" alt="..." width="800" height="600">This is the cheapest, highest-leverage optimization on the web. One attribute, ~70% of users covered, no JavaScript required. The width/height attributes are critical — without them, lazy-loaded images cause layout shifts (Cumulative Layout Shift) when they finally load.
Critical caveat: do not lazy-load above-the-fold images, especially the LCP image. Lazy-loading the hero image makes LCP worse, not better, because it adds an extra browser decision step before the LCP element is fetched.
2. Native iframe lazy loading
Same attribute, applied to iframes:
<iframe src="https://youtube.com/embed/..." loading="lazy"></iframe>This matters most for pages with embedded YouTube videos, maps, or third-party widgets — each iframe pulls down hundreds of KB and dozens of subrequests. Lazy-loading them can cut initial page weight by megabytes.
3. JavaScript module lazy loading (route-based code splitting)
Single-page applications use dynamic import() to load route-specific code on demand:
const Settings = lazy(() => import('./Settings'));The settings page bundle isn't fetched until the user navigates there. Combined with route-based splitting in webpack, Vite, or Rollup, this keeps the initial JS bundle small. Without this, every SPA route ships in the initial bundle, which hurts TTI on every page.
4. IntersectionObserver-based custom lazy loading
For things native lazy loading doesn't cover — third-party scripts, custom video players, complex components — IntersectionObserver lets you trigger work when an element scrolls near the viewport:
const obs = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadComponent(entry.target);
obs.unobserve(entry.target);
}
});
}, { rootMargin: '200px' });The rootMargin: '200px' trick pre-fetches resources 200px before they enter the viewport, so the user rarely sees a loading flash. This is how mature lazy-load implementations feel snappy rather than janky.
What lazy loading actually buys you (numbers)
Real numbers from production sites we've measured:
- Image-heavy pages: 40-70% reduction in initial bytes when below-fold images are deferred.
- LCP improvement: 200-1500ms on pages where the LCP element competed with deferred-but-non-lazy images. The browser preload scanner stops fighting for bandwidth.
- SPA initial bundle reduction: 50-80% with route-based code splitting. A 3MB monolithic bundle becomes a 600KB initial bundle plus per-route chunks.
- Mobile data savings: Users who bounce before scrolling never download below-fold content. For news/blog sites with high bounce rates, this is meaningful for both ad-supported revenue and user goodwill.
Common lazy loading mistakes
- Lazy-loading the LCP image. The single biggest mistake. Use
fetchpriority="high"on the hero image and reserveloading="lazy"for below-fold content. Run a speed test to see which image is your LCP element. - Forgetting width/height attributes. Lazy-loaded images without explicit dimensions cause layout shifts as they load. CLS regressions hurt your Core Web Vitals score.
- Lazy-loading too aggressively. Setting
rootMargin: '0px'or no margin causes a flash of empty space when content scrolls in. Use a 200-500px buffer. - Lazy-loading critical fonts. Custom fonts in your initial render path should not be deferred. Use
font-display: swapand preload the critical font instead. - Mistaking lazy loading for performance. Lazy loading defers work; it doesn't eliminate it. If your below-fold scripts are slow, lazy-loading them just delays the slowness rather than fixing it.
- Using JavaScript libraries when native works. If your audience is on modern browsers (95%+ now), the native
loading="lazy"attribute beats every JavaScript lazy-load library. Less code, fewer bugs, no library to maintain.
Lazy loading and SEO
Search engines render JavaScript, but they don't infinitely scroll. If your content is gated behind lazy loading that requires scrolling to trigger, Google may never see it. Best practices:
- Use native
loading="lazy"for images — Googlebot handles it correctly. - For content that must be indexed, render it server-side. Don't lazy-load body copy.
- For carousel/tabs content, render all panels in HTML and use CSS to hide non-active ones. Don't lazy-load tab content via JavaScript or it won't be indexed.
- For infinite scroll, provide pagination links as a fallback so crawlers can follow them.
Testing lazy loading
Three tools to verify lazy loading is working:
- Chrome DevTools Network tab. Reload the page. You should see images NOT in the initial waterfall, then loading as you scroll.
- Lighthouse / PageSpeed Insights. The "Defer offscreen images" audit catches missed opportunities.
- Real device testing. Throttle your connection to Slow 4G in DevTools and confirm the page is usable before below-fold content arrives.
FAQ: Lazy Loading
Should I use loading="lazy" on every image?
No. Above-the-fold images, especially your LCP element, should NOT be lazy-loaded. Use fetchpriority="high" on critical images and reserve lazy loading for below-fold content.
Does lazy loading hurt SEO?
Not when implemented correctly. Native loading="lazy" is fully supported by Googlebot. Custom IntersectionObserver-based lazy loading is also fine for images. Avoid lazy-loading body text or critical content unless you have a server-side rendered fallback.
What's the browser support for native lazy loading?
Excellent — Chrome 76+, Edge 79+, Firefox 75+, Safari 15.4+, all of which represent ~95%+ of global users in 2026. Older Safari users get all images loaded immediately, which is a graceful degradation rather than a broken experience.
How is lazy loading different from code splitting?
Code splitting is the build-time process of breaking your bundle into chunks. Lazy loading is the runtime decision to fetch a chunk only when needed. Modern bundlers do both — they split your code AND configure dynamic import() calls so chunks load on demand.
Can lazy loading make pages feel slower?
Yes, if implemented badly. Loading flashes, layout shifts, and missing content during scroll all hurt perceived performance. Use rootMargin to pre-fetch early, set explicit dimensions on images, and skeleton/placeholder UI for components that take time to load.
What about lazy-loading background images in CSS?
CSS background-images don't support native lazy loading. Either inline them as <img> with loading="lazy" (best), use IntersectionObserver to add the background-image style on scroll, or accept that all backgrounds load upfront.
How LoadFocus measures lazy loading impact
Lazy loading optimizations show up in real-world metrics. Run a website speed test to see which images are eligible for lazy loading and confirm your LCP element is properly prioritized. Use load testing to validate that your pages still perform well under high concurrent traffic — sometimes lazy loading shifts work to be triggered by user behavior in ways that change your backend traffic patterns.
Related LoadFocus Tools
Put this concept into practice with LoadFocus — the same platform that powers everything you just read about.