Back to Blog
Image Optimization

Lazy Loading: How Loading 100 Images Slower Made Page Load Faster

Blog had 100 images per page. Load time: 12 seconds. Added lazy loading. Load time: 2 seconds. Here is how it actually works.

Alex Rivera7 min readGlobal
Website performance optimization
Website performance optimization
Tool Pairing

Use Image Kit while this guide is open

Compress, convert, and optimize the same image assets discussed in this guide.

Open Image Kit
Advertisement

Geographic Note: Lazy loading is a universal web performance technique applicable globally.

100 Images Per Page: From 12 Seconds to 2 Seconds

Built photography blog. Each post had 50-100 images. Page load time: 12 seconds. Google PageSpeed score: 23/100. Users complained. Bounce rate was terrible. Added lazy loading. Everything changed.

The Problem With Loading Everything

My approach:

Put 100 images in blog post.

Browser loads all 100 immediately when page opens.

What actually happens:

User opens page.

Browser starts downloading:

  • Image 1 (2MB)
  • Image 2 (1.8MB)
  • Image 3 (2.1MB)
  • ...
  • Image 100 (1.9MB)

Total: 180MB to download.

On slow connection: takes forever.

The reality:

User sees maybe 3 images on first screen.

Other 97 images are below the fold.

But browser downloaded all 100 anyway.

Waste of bandwidth. Waste of time.

Before vs After: Performance & User Experience Impact

Lazy loading performance improvement
Figure 1: Lazy loading impact on page performance
MetricBefore Lazy LoadingAfter Lazy LoadingImprovementImpact
Initial Page Size180MB (100 images)5MB (3 visible images)-97%Massive bandwidth savings
Initial Load Time12 seconds2 seconds-83%Much faster first paint
PageSpeed Score23/100 (fail)84/100 (good)+265%Passes Core Web Vitals
Mobile Bounce Rate60% (terrible)15% (good)-75%Users actually stay
Desktop Bounce Rate35%18%-49%Better experience
Images Loaded (avg visit)100 (all, wasted)15 (only viewed)-85% wasteEfficient loading
Bandwidth per Visit180MB9MB-95%Mobile-friendly
LCP (Largest Paint)8.5 seconds2.1 seconds-75%Google Core Web Vital โœ“
User ComplaintsFrequentNone100% reductionHappy users
Loading diagram...
12 sec
Load time before
2 sec
Load time after

Cost Savings (10,000 monthly visitors):

  • Before: 1.8TB bandwidth, $144/month at $0.08/GB
  • After: 90GB bandwidth, $7.20/month
  • Monthly savings: $136.80
  • Annual savings: $1,641.60

What Lazy Loading Actually Does

Simple concept:

Only load images that are visible.

Load other images when user scrolls to them.

Example:

Page with 100 images.

User opens page:

  • Load images 1-3 (visible)
  • Don't load images 4-100 (not visible yet)

User scrolls down:

  • Load next 5 images as they come into view
  • Still don't load the rest

Result:

Initial page load: 3 images (5MB) instead of 100 images (180MB)

Page loads in 2 seconds instead of 12.

Eager vs Lazy Loading: Technical Comparison

FactorEager Loading (default)Lazy Loading (loading="lazy")Winner
Initial LoadAll images immediatelyOnly visible images๐Ÿ† Lazy
Bandwidth Usage100% upfront10-20% initially, rest on-demand๐Ÿ† Lazy
Time to InteractiveDelayed by all imagesFast, only loads essential๐Ÿ† Lazy
User ExperienceSlow start, smooth scrollFast start, loads during scroll๐Ÿ† Lazy
SEO ImpactNeutralNeutral (Google renders all)๐Ÿค Tie
ImplementationNo code neededAdd loading="lazy"๐Ÿ† Lazy (simple)
Browser Support100%97% (fallback to eager)๐Ÿค Tie
Mobile Data SavingsNone85-95% savings๐Ÿ† Lazy
Above-the-foldGoodBad (causes delay)๐Ÿ† Eager
Below-the-foldWastefulEfficient๐Ÿ† Lazy
PageSpeed ScoreLower (loads everything)Higher (optimized loading)๐Ÿ† Lazy
Core Web VitalsOften failsUsually passes๐Ÿ† Lazy

When to Use Each:

ScenarioUse Eager LoadingUse Lazy LoadingReason
Above the fold imagesYesNoUser sees immediately
Below the fold imagesNoYesUser may never scroll
Hero imageYesNoCritical for LCP
Gallery imagesNoYesMany images, scroll-based
Logo, iconsYesNoPart of UI
Product thumbnailsNo (except first)YesGrid layout
Blog content imagesFirst 1-2:Rest:Progressive loading
Infinite scrollNeverAlwaysPerfect use case
Page with 3 imagesYesNot neededOverhead not worth it
Page with 50+ imagesNeverYesCritical optimization

My First Lazy Loading Attempt (Failed)

Found JavaScript library. Installed it.

<script src="lazy-load-library.js"></script>

What went wrong:

Page loaded blank. No images showed up.

Checked console. Error: "Lazy load library failed to initialize"

Spent 2 hours debugging.

The problem:

Library conflicted with another script.

Also, library was 87KB. Added to page weight.

For something that should be simple.

The Native Lazy Loading Solution

Native lazy loading implementation
Figure 2: Browser native lazy loading support

Turns out browsers have built-in lazy loading now.

How to use:

<img src="photo.jpg" loading="lazy" alt="description">

That's it. One attribute.

What it does:

Browser automatically:

  • Loads images near viewport
  • Defers images far from viewport
  • Loads deferred images when user scrolls near them

No JavaScript needed.

No external library.

Works in Chrome, Firefox, Edge, Safari.

The Results

Before lazy loading:

  • Page size: 180MB
  • Load time: 12 seconds
  • PageSpeed score: 23/100
  • Users complained about slow loading

After adding loading="lazy" to all images:

  • Initial page size: 5MB (3 visible images)
  • Load time: 2 seconds
  • PageSpeed score: 84/100
  • Complaints stopped

Same page. Same content. One HTML attribute.

When Lazy Loading Broke Things

Added lazy loading to every image.

Including:

  • Logo in header
  • Hero image at top of page
  • Icons in navigation

What happened:

Page loaded with blank spaces.

Images popped in after a delay.

Looked broken.

The problem:

Images above the fold shouldn't be lazy loaded.

User sees them immediately. They should load immediately.

The fix:

<!-- Above the fold: load immediately -->
<img src="logo.jpg" alt="Logo">
<img src="hero.jpg" alt="Hero image">

<!-- Below the fold: lazy load -->
<img src="gallery-1.jpg" loading="lazy" alt="Gallery photo 1">
<img src="gallery-2.jpg" loading="lazy" alt="Gallery photo 2">

Rule: First 2-3 images load normally. Rest lazy load.

The Placeholder Strategy

Lazy loaded images create problem:

Browser doesn't know image dimensions until it loads.

Page layout shifts as images load. Annoying.

Solution 1: Set width/height attributes

<img src="photo.jpg" loading="lazy" width="800" height="600" alt="Photo">

Browser reserves space. No layout shift.

Solution 2: Use aspect ratio CSS

.image-container {
  aspect-ratio: 16/9;
}

Works for responsive images.

Solution 3: Blur placeholder

Show blurry thumbnail immediately.

Load full image lazily.

Looks better than blank space.

I use width/height method. Simplest.

Placeholder Strategy Comparison

StrategyImplementationUX / PerformanceCLS & Rating
No placeholderNothingLayout shifts / FastPoor CLS - Avoid
Width/Height attrswidth="800" height="600"No shifts / FastPerfect CLS - Best
Aspect ratio CSSaspect-ratio: 16/9No shifts / FastPerfect CLS - Great
Solid colorbackground: #f0f0f0Basic / FastGood CLS - OK
Blur placeholderTiny blur thumbnailLooks pro / +50msGood CLS - Good
SVG placeholderVector placeholderCrisp / FastGood CLS - OK
Loading spinnerAnimated iconDistracting / Extra CSSGood CLS - Poor

Implementation Examples:

MethodFile SizeLoad TimeCLS Impact
Width/Height (recommended)0 bytes0ms0 shift
Aspect Ratio CSS50 bytes CSS0ms0 shift
Blur-up+5KB+50msMinor shift
No strategy0 bytes0msMajor shift

Lazy Loading for Different Image Types

Blog post images:

Lazy load everything except first image.

<img src="featured.jpg" alt="Featured"> <!-- Load immediately -->
<img src="content-1.jpg" loading="lazy" alt="...">
<img src="content-2.jpg" loading="lazy" alt="...">

Product gallery:

First product image loads immediately.

Thumbnails lazy load.

<img src="product-main.jpg" alt="Main product photo">
<img src="thumb-1.jpg" loading="lazy" alt="Angle 1">
<img src="thumb-2.jpg" loading="lazy" alt="Angle 2">

Infinite scroll:

All images lazy load.

New images load as user scrolls.

Perfect use case.

Lazy Loading Strategy by Image Type

Image TypeFirst ImageRemaining ImagesReasonExpected Savings
Blog PostEager loadLazy load allFeatured shows fast90-95%
Product GalleryMain: EagerThumbnails: LazyMain product critical85-90%
Photo GalleryFirst 3: EagerRest: LazyInitial grid visible80-85%
Infinite ScrollNone eagerAll lazyScroll-based loading95-98%
Hero SliderFirst slide: EagerOther slides: LazyAuto-play delay70-80%
Thumbnail GridRow 1: EagerRows 2+: LazyAbove fold visible75-85%
Article SidebarNoneAll lazyBelow main content90-95%
Footer ImagesNoneAll lazyBottom of page98-99%
Background PatternsEager loadN/ACSS, can't lazy load0%
AdsFirst ad: EagerLower ads: LazyRevenue optimization60-70%

Priority Matrix:

Image LocationLoad StrategyPriorityLCP Impact
Above fold, LCP candidateEager, high priorityCriticalDirect
Above fold, non-LCPEager, normal priorityHighIndirect
Just below foldLazy, normal priorityMediumNone
Mid-pageLazy, low priorityLowNone
Bottom of pageLazy, low priorityLowNone
Off-screen sliderLazy until neededLowNone

Background Images Problem

Lazy loading attribute only works on <img> tags.

Doesn't work on CSS background images.

Doesn't work:

.hero {
  background-image: url('big-image.jpg');
}

Loads immediately. Can't lazy load.

Workaround:

Use <img> instead of background image.

Or use JavaScript library for background image lazy loading (adds complexity).

I converted most backgrounds to <img> tags. Simpler.

The Loading Threshold

Lazy loading doesn't wait until image is in viewport.

Loads slightly before.

Default behavior:

Browser starts loading when image is ~1000px from viewport.

User scrolling down: image loads before they see it.

Smooth experience. No waiting.

Why this matters:

Slow connection: image might not load in time.

User sees placeholder briefly.

Can adjust threshold (with JavaScript) but default works well.

Mobile vs Desktop

Lazy loading matters more on mobile.

Mobile:

  • Slower connection
  • Limited data plans
  • Less powerful device

Loading 180MB of images on mobile: kills experience.

Desktop:

  • Faster connection
  • Unlimited data
  • More powerful

Still benefits from lazy loading but less critical.

My stats:

Mobile users: 60% bounce rate before lazy loading.

After lazy loading: 15% bounce rate.

Desktop: improvement but less dramatic.

LCP and Lazy Loading

LCP = Largest Contentful Paint (Core Web Vital)

Measures when main content loads.

Mistake I made:

Lazy loaded hero image (largest image on page).

LCP got worse. Hero image loaded late.

The rule:

Don't lazy load anything above the fold.

Especially don't lazy load largest image on page.

Google specifically warns about this.

Lazy Loading Impact on Core Web Vitals

| Core Web Vital | Before โ†’ After | Impact / Google Threshold | Pass/Fail | |---------------|-------------|---------------------------|-----------|| | LCP (Largest Contentful Paint) | 8.5s โ†’ 2.1s | -75% / <2.5s = Good | Pass | | FID (First Input Delay) | 180ms โ†’ 45ms | -75% / <100ms = Good | Pass | | CLS (Cumulative Layout Shift) | 0.42 โ†’ 0.02 (with placeholders) | -95% / <0.1 = Good | Pass | | INP (Interaction to Next Paint) | 250ms โ†’ 120ms | -52% / <200ms = Good | Pass | | TTFB (Time to First Byte) | 420ms โ†’ 380ms | -10% / <800ms = Good | Pass | | FCP (First Contentful Paint) | 3.2s โ†’ 1.1s | -66% / <1.8s = Good | Pass |

Common Mistakes That Hurt Core Web Vitals:

MistakeLCP ImpactCLS ImpactFixHow Common
Lazy load hero image+3-5sNoneRemove lazy from LCP imageVery common
No width/heightNone+0.3-0.5Add dimensionsVery common
Lazy load above fold+1-3sNoneOnly lazy load below foldCommon
Over-aggressive lazy+0.5-1sNoneIncrease thresholdRare
No preconnect to CDN+0.3-0.8sNoneAdd <link rel="preconnect">Common

Lazy Loading for Ads

Blog has ads.

Tried lazy loading ads (load when user scrolls to them).

What happened:

Ad revenue dropped 30%.

Why:

Ads that don't load don't show.

If user bounces before scrolling, ads never loaded.

Never counted as impression.

The fix:

Load first ad immediately.

Lazy load ads further down page.

Revenue recovered.

Testing Lazy Loading

Tools I used:

Chrome DevTools:

Network tab โ†’ Throttle to "Fast 3G"

See how page loads on slow connection.

Before: everything loads slowly.

After: visible content loads fast, rest loads as needed.

PageSpeed Insights:

Before: 23/100 (terrible)

After: 84/100 (good)

Specifically mentions: "Defer offscreen images" โœ“ passing

Real user testing:

Asked friends on different connections to test.

Everyone reported much faster load.

When Not to Lazy Load

Small pages with few images:

Page with 3 images total?

All above the fold?

Don't lazy load. Unnecessary.

Critical images:

  • Logo
  • Hero image
  • Product main photo
  • Anything above the fold

Images needed for functionality:

Image-based navigation.

Slideshow that auto-plays.

These need to load immediately.

The Progressive Loading Strategy

For image-heavy pages, I use progressive approach:

Priority 1: Immediate load

  • First 1-2 images (above fold)

Priority 2: Lazy load (near viewport)

  • Images 3-10 (load="lazy")

Priority 3: Lazy load (far from viewport)

  • Images 11+ (load="lazy")

Browser handles this automatically.

Just add attribute to images that should wait.

Browser Support Reality

Native lazy loading support:

  • Chrome: โœ“ (since 2019)
  • Firefox: โœ“ (since 2020)
  • Edge: โœ“ (since 2020)
  • Safari: โœ“ (since 2021)

Old browsers?

Ignore loading="lazy" attribute. Load images normally.

Progressive enhancement. Doesn't break anything.

Browser Support & Fallback Strategy

BrowserVersion / SupportMarket ShareFallback / Notes
Chrome76+ (2019) / Full63%First to support
Firefox75+ (2020) / Full3%Native support
Edge79+ (2020) / Full5%Chromium-based
Safari15.4+ (2021) / Full20%Latest versions
Opera64+ (2019) / Full2%Chromium-based
Samsung Internet12+ (2020) / Full3%Mobile browser
Safari iOS15.4+ (2021) / Full18%iPhone/iPad
Chrome Android76+ (2019) / Full45%Most Android
IE 11All / No<1%Loads all (ignore attribute)
Old Safari<15.4 / No2%Loads all (progressive enhancement)
Total Support-~98%Graceful degradation, no breaking changes

Polyfill/Fallback Options:

ApproachProsConsFile SizeMy Rating
Do nothingSimple, progressive enhancement2% don't get optimization0KBBest
JS polyfill100% supportAdds 3-8KB, complexity3-8KBOK if needed
Intersection ObserverCustom controlWrite your own code1-2KBOK for advanced
Heavy libraryFeature-richOverkill, 50KB+50KB+Avoid

My Recommendation: Use native lazy loading without polyfill. 98% support is enough, old browsers still work (just load all images).

Impact on SEO

Google can't index images that don't load.

Worried lazy loading would hurt SEO.

Tested:

Lazy loaded images still appear in Google Images.

Still get traffic from image search.

How Google handles it:

Google renders JavaScript.

Loads lazy images during indexing.

Sees all images even if lazy loaded.

No SEO penalty observed.

The Bandwidth Savings

Blog with 50 posts.

Each post: 100 images, 180MB.

Before lazy loading:

User reads 5 posts.

Downloads: 900MB (most images never seen)

After lazy loading:

User reads 5 posts.

Downloads: 45MB (only images actually viewed)

95% bandwidth reduction.

For mobile users with limited data: huge difference.

My Current Implementation

Every blog post:

<!-- Featured image (above fold): eager load -->
<img src="featured.jpg" width="1200" height="800" alt="Featured photo">

<!-- Content images (below fold): lazy load -->
<img src="photo-1.jpg" loading="lazy" width="800" height="600" alt="Description 1">
<img src="photo-2.jpg" loading="lazy" width="800" height="600" alt="Description 2">
<!-- etc -->

That's the entire implementation.

No JavaScript. No library. Just HTML attribute.

Bottom Line

Lazy loading is simplest performance optimization.

How to do it:

Add loading="lazy" to images below the fold.

Don't add it to images above the fold.

That's it.

Results:

  • Faster initial page load
  • Less bandwidth used
  • Better PageSpeed scores
  • Happier users

Time to implement:

Find all <img> tags below fold.

Add loading="lazy".

Maybe 10 minutes for entire site.

My results:

12 second load โ†’ 2 second load

23 PageSpeed score โ†’ 84 PageSpeed score

60% mobile bounce rate โ†’ 15% mobile bounce rate

One HTML attribute changed everything.

Complete Lazy Loading Implementation Workflow

Loading diagram...

Implementation Time Breakdown:

StepTime RequiredDifficultyTools Needed
1. Audit images5-10 minEasyBrowser DevTools
2. Identify above fold10-15 minEasyBrowser resize
3. Add lazy loading15-30 minEasyFind/replace in editor
4. Add placeholders20-40 minMediumMeasure image dimensions
5. Test15-20 minMediumDevTools Network tab
6. Measure impact10 minEasyPageSpeed Insights
7. Verify CLS10 minMediumDevTools Performance
Total85-145 min~2 hoursFree browser tools

ROI Calculation:

  • Time invested: 2 hours one-time
  • Bandwidth savings: 85-95% ongoing
  • PageSpeed improvement: 23 โ†’ 84 (typical)
  • Bounce rate reduction: 40-60%
  • Payback: Immediate, benefits ongoing

Your Lazy Loading Implementation Checklist

PhaseTaskTime / PriorityImpactNotes
๐Ÿ” AuditCount images per page5 min / CriticalFoundationCheck all page types
Identify image-heavy pages5 min / CriticalTargetBlog, gallery, products
Check current PageSpeed score3 min / HighBaselineBefore/after comparison
๐Ÿ“ IdentifyOpen page, note viewport size2 min / CriticalAccuracyTest at 1920ร—1080
Mark above-fold images5 min/page / CriticalCriticalNever lazy load these
Find LCP image (DevTools)3 min / CriticalPerformanceHero/largest image
โœ๏ธ ImplementAdd loading="lazy" below fold10-30 min / CriticalMain benefitAll images except above fold
Add width and height20-40 min / CriticalPrevent CLSEvery lazy image
Keep hero/logo eager2 min / CriticalLCPDon't lazy load
๐Ÿงช TestDevTools Network: Reload3 min / CriticalVerifyOnly 2-5 images load
DevTools Network: Scroll5 min / CriticalVerifyImages load on scroll
Test on real mobile device5 min / HighMobile experienceCheck 3G/4G
Test in Safari3 min / MediumCompatibilityiOS support
MeasureRun PageSpeed Insights3 min / CriticalQuantifyBefore/after
Check "Defer offscreen images"1 min / HighValidationShould pass
Measure LCP2 min / CriticalCore Web VitalShould improve
Check CLS score3 min / CriticalLayout shiftsShould be <0.1
๐Ÿ”ง FixIf LCP worse: remove lazy from hero2 min / If neededCritical fixCommon mistake
If CLS high: add dimensions15 min / If neededLayout fixMeasure all images
If images don't load: check syntax5 min / If neededDebugQuotes, spelling
๐ŸŽฏ FinalizeDeploy to production10 min / CriticalGo liveAfter testing
Monitor for 1 week10 min/day / HighCatch issuesUser feedback
Measure bandwidth savings5 min / MediumQuantify ROIAnalytics

Quick Start (Minimum Viable - 30 minutes):

  1. Add loading="lazy" to all images except first 2 (15 min)
  2. Test with DevTools Network tab (10 min)
  3. Check PageSpeed score (5 min)
  4. Result: 80-90% bandwidth savings

Full Implementation (Recommended - 2 hours):

  1. Everything above PLUS:
  2. Add width/height to all images
  3. Test on multiple devices
  4. Optimize LCP and CLS
  5. Result: 95% bandwidth savings + perfect Core Web Vitals

Common Issues & Quick Fixes:

IssueCauseFixTime
Hero image blankLazy loaded LCPRemove loading="lazy" from hero30 sec
Layout jumpsNo dimensionsAdd width and height attributes2 min/image
Images never loadSyntax errorCheck quotes: loading="lazy" not loading='lazy'30 sec
All load at onceAll above foldNormal behavior, add more contentN/A
Old Safari doesn't workNo support <15.4Expected, degrades gracefullyN/A

Data Sources

This article is based on global best practices and standards:

Note: Tool availability, features, and best practices may vary by region. Always verify compliance with local regulations before using any service.


Related Reading

Explore more image optimization articles to improve your website performance:

Advertisement
Next Reading

Related guides worth indexing with this article

See all articles