- ⚠️ Images using
loading="lazy"may still load even when styled withdisplay:none. - 🧪 Browser behavior varies: Chrome often fetches hidden lazy images, Firefox defers more consistently.
- 🧠 Relying solely on
display:nonefor deferral is unreliable across rendering conditions. - 🔍 IntersectionObserver provides more accurate image load triggers based on actual visibility.
- 📉 Lazy loading can hurt LCP scores if misused on important images above the fold.
Web performance often depends on small, smart changes. Lazy loading is one example. It seems simple but makes a big difference. But what happens when an image with loading="lazy" is also hidden with display:none? If you want to improve Core Web Vitals, or just make pages feel faster, you need to understand this. We will look at how modern browsers handle this. And then we will show you how to control lazy image loading.
Understanding loading="lazy": More Than Just a Hint
The HTML loading="lazy" attribute lets developers delay when images load. These images load only when they are close to the user's screen. This makes the initial page size smaller. It also uses fewer resources that block rendering, and it speeds up how fast a page becomes interactive.
<img src="image.jpg" loading="lazy" alt="Optimized Lazy Image" />
Here is what happens inside:
- The browser reads the image code but does not download the image right away. It waits unless the image is needed (for example, if it affects the page layout or will be visible soon).
- Images not on screen or at the bottom of the page wait. They load only when the user scrolls or when layout changes make them visible.
📌 Google Chrome Developers reported that native lazy loading on Chrome cut median image load times by 10%. It also saved a lot of bandwidth for users with slow connections.
Support for this feature changes slightly but is broadly available in modern versions of Chrome, Edge, Firefox, and Safari.
Clarifying display:none: Hidden But Present
The CSS display:none rule fully takes an element out of the document's layout and rendering. This is different from visibility:hidden. That rule keeps the element in the layout, but you cannot see it. However, display:none makes the rendering engine ignore the element completely.
<img src="image-1.jpg" loading="lazy" style="display:none;" />
But display:none does not take the element out of the Document Object Model (DOM). The browser still reads it. You can still reach it with JavaScript. And you can style or unstyle it later. This difference is important. It helps us see how a browser might still check its attributes, like loading="lazy", even if it is not showing now.
The Dangerous Assumption: Lazy + display:none
Many developers make a common mistake. They assume that using loading="lazy" and display:none together will stop images from loading until they are made visible. But this idea can hurt your site's performance badly.
In practice, this mix gives different and unreliable results:
- Some browsers follow
display:none. They wait to load images, even if they are marked lazy. - But other browsers ignore this styling completely. They start loading images anyway.
Here is how browsers work:
- Chrome: It will often load images with
loading="lazy", even when they are hidden withdisplay:none. Chrome decides to load images based on things like page layout or the DOM structure. - Firefox: This browser waits to load images more reliably. It respects hidden states for a longer time.
- Safari: This browser is careful. It loads images less often. Sometimes it waits for you to scroll or for a visual layout change.
📌 The Web Almanac (2023) found that Firefox waited on 23% more
display:nonelazy-loaded images than Chrome did. This shows a big difference between browsers.
These differences matter a lot for pages where speed is key, like landing pages, checkout pages, or mobile news articles. On these pages, large main images could be handled wrong across different devices and browsers.
Browser Rendering Variations: Why Predictability Fails
Every browser uses slightly different rules and timing to decide if an image should load. This can depend on:
- Screen size and resolution
- When the DOM puts the image in (from server or client)
- The user’s device, network speed, or data saver settings
- The order JavaScript runs, which changes what you see
- Layout changes or reflows that alter what is visible
Because browsers are not all the same, relying on display:none to control lazy loading is a bad idea. An image hidden with CSS might still load if any of these things happen to align unfavorably.
Practical Experiment: Testing Lazy + Hidden Images
Do you want to test this yourself? Follow these steps in Chrome:
-
Put this HTML code in:
<img src="https://example.com/sample.jpg" loading="lazy" style="display:none;" /> -
Open Chrome DevTools. Go to the Network tab.
-
Reload the page. Filter for “sample.jpg”.
-
Watch if, when, and how the image is asked for.
Do this again in Firefox and Safari to compare how they act. The results will differ. And surprisingly, even the order of script execution on your page could change if the image loads or not. For better control, try logging script execution and mutation observers on your test page.
Hidden but Loaded: Common Real-World Triggers
Some common events can make browsers load display:none lazy images, even if you do not want them to:
- Framework setup: React, Vue, or similar tools make pages on the server. Here, what you see changes fast when updates happen. This loads images that were hidden before.
- Class switching: JavaScript can remove CSS classes (like
.hidden). This can make browsers load the image in the same screen update. - Scroll loading ahead: Some browsers guess where you will scroll next. They might load images not on screen now, even ones hidden from the page layout.
So, what is a safer way?
Do not just use display:none to control lazy loading. Instead, use methods that delay when the DOM includes the image, or delay its visibility or opacity. Use ways that browsers respect:
Reliable Alternatives to display:none
1. Intersection Observer API
Use JavaScript to watch when an image's parent box enters the screen. Only THEN add the actual <img> tag.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = document.createElement('img');
img.src = "image.jpg";
img.alt = "Headline";
entry.target.appendChild(img);
observer.unobserve(entry.target);
}
});
});
observer.observe(document.querySelector('#img-container'));
This makes sure the image is not part of the document's first read. It also means the image will not load until it is truly needed.
2. Delay DOM Insertion
By not showing the image at all (meaning it is not in the HTML or initial JSX), you control exactly when it gets added:
<!-- Only insert <img> after user triggers, such as scroll or interaction -->
This method also works well for static site generation (SSG) and pages made on the server.
3. Use CSS Strategies That Avoid Display Removal
If you want to hide something without taking it out of the layout, use:
visibility: hiddenopacity: 0- or put the image off the screen using
position: absolute; left: -9999px;
These make the element unseen. But they keep it in the layout calculations. Some browsers see this as a sign not to start getting images yet.
Framework Patterns: React & Vue Lazy Strategies
In JavaScript frameworks, you can combine lazy loading with conditional rendering and lifecycle events more simply.
React Component with Intersection Lazy Load
function LazyImage({ src, alt }) {
const [visible, setVisible] = useState(false);
const ref = useRef();
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setVisible(true);
}
});
const current = ref.current;
if (current) observer.observe(current);
return () => {
if (current) observer.unobserve(current);
};
}, []);
return (
<div ref={ref}>
{visible && <img src={src} alt={alt} />}
</div>
);
}
With server-side rendering (SSR), this stops images from loading until after hydration and visibility checks are done.
Accessibility and Progressive Enhancement
Lazy loading should never hurt accessibility. For screen readers or users with JavaScript turned off:
- Always add
<noscript>fallbacks for any lazy content that is very important (for example, the main section, or key branding):
<noscript>
<img src="fallback-hero.jpg" alt="Welcome Banner" />
</noscript>
- Always use
altattributes, whether the image is lazy loaded or not. - Delay less important visuals, but never texts, icons, or images that serve a purpose (like charts).
Performance Optimization Tips
Here is how to make a good lazy loading plan:
- ✅ Do not use
display:none. Instead, delay when the DOM actually shows the image. - ✅ Save space for large images. Use
widthandheightattributes to stop layout changes. - ✅ Use
fetchpriority="high"or do not lazy load important images at the top of the page. This helps LCP scores. - ✅ When you lazy load images, record the real network requests before and after scrolling.
Lazy Load Images and Core Web Vitals: Proceed Strategically
Lazy loading saves bandwidth. But if you use it carelessly, it can hurt Core Web Vitals. This is a key factor in Google rankings:
- 🔴 LCP (Largest Contentful Paint) gets worse if main images use
loading="lazy". This is because they load too late. - ⚠️ CLS (Cumulative Layout Shift) gets higher if lazy images do not have set sizes. This makes content move when the images load.
📌 A study in HTTP Archive’s 2022 report found that 73% of the best-performing pages with better LCP had adjusted their image loading carefully. They avoided lazy loading images at the top of the page.
Always check your lazy load images method. Use tools like Lighthouse and Pagespeed Insights. Also check real-world data like CRuX.
Summary: Don’t Mix Without Testing
Mixing loading="lazy" and display:none can give strange and wasteful results. Relying on CSS visibility instead of controlling the DOM means images will load inconsistently. It also means fetch times will be unpredictable across browsers.
The main rule: only put visible images into the DOM.
Devsolus Cheat Sheet: Lazy Load + Visibility Handling
| Situation | Recommended Strategy |
|---|---|
| Hidden image during SSR | Defer DOM insertion until hydrated |
| Offscreen image | Use loading="lazy" and ensure it’s visible |
| Accessibility concerns | Add <noscript> image fallback |
| Precision control needed | Use IntersectionObserver |
Devsolus Quick Reference: Best Practices Checklist
✅ Don’t rely on display:none for lazy-loading control
✅ Use IntersectionObserver for real visibility tracking
✅ Render image tags conditionally in components
✅ Monitor web performance with Core Web Vitals
✅ Explicitly test image fetching behavior in all target browsers
✅ Use noscript for graceful degradation
Do you have a favorite way to stop hidden images from downloading? Share your ideas with the Devsolus community.
Citations
- Google Chrome Developers. (2019). Native Lazy-Loading for the Web.
- Web Almanac by HTTP Archive. (2023). 2023 Performance Report.
- HTTP Archive. (2022). Core Web Vitals Performance Report.