Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →For ordinary off-screen images, use the browser’s native loading="lazy" attribute first. Use JavaScript with the Intersection Observer API when you need custom loading behavior, or when the resource is something native image lazy loading does not cover, such as a CSS background image. Keep hero images eager and reserve space for every deferred image so loading them does not push page content around.
Use native lazy loading for ordinary images
The simplest way to defer a standard HTML image is to add loading="lazy" to its <img> element:
<img
src="/images/photo.jpg"
loading="lazy"
width="800"
height="600"
alt="A description of the photo"
>
The browser treats loading as a hint. With lazy, it schedules the image for loading when it is within a browser-calculated distance of the viewport; it does not necessarily wait until the image is exactly visible. The distance and timing are browser-managed, so do not rely on a precise scroll position.
For a normal image that should be requested immediately, use loading="eager" or omit the attribute. In particular, leave a hero image or another image likely to appear above the fold eager. Lazy loading can delay discovery of an important image while the browser works out the page layout, whereas an image present in the initial markup can be discovered earlier.
Recommended Free Tools
#1 Best Overall
Give the browser dimensions
Set width and height to the image’s intrinsic dimensions, or reserve its correct aspect ratio with CSS. A not-yet-loaded lazy image can otherwise have no space reserved, and the page may reflow when the image arrives. MDN recommends explicit dimensions for images generally, and calls them especially important for lazy-loaded images.
<img
src="/images/article-photo.jpg"
loading="lazy"
width="1200"
height="800"
alt="A cyclist on a forest trail"
>
The browser can use those dimensions to calculate the image’s space before fetching it. If you use CSS for layout instead, preserve the same aspect ratio so the eventual image does not unexpectedly change the page geometry.
When JavaScript and Intersection Observer are useful
For ordinary <img> elements, a custom loader usually duplicates work browsers already provide. Use the Intersection Observer API when you need application-controlled behavior, or when the deferred resource is not an ordinary image request covered by loading="lazy"—for example, a CSS background image or a video poster.
Intersection Observer asynchronously reports when a target intersects a viewport or a specified ancestor. A common pattern keeps a real image URL in a data attribute, observes each image, assigns the URL when the image approaches the viewport, and stops observing it after that assignment.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesRank #2
A minimal observer for deferred images
Use this pattern when your page has image elements with data-src instead of an active src:
<img
data-src="/images/gallery-1.jpg"
width="800"
height="600"
alt="A mountain lake"
>
<script>
const observer = new IntersectionObserver((entries, observer) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
document.querySelectorAll('img[data-src]').forEach((img) => {
observer.observe(img);
});
</script>
This is a minimal example, not a complete production image pipeline. It assumes the observed elements are images with a usable data-src value and that they are in the document when the selection runs. It also does not define custom loading, error, or responsive-source behavior. Preserve dimensions and a suitable fallback for your page, and extend the pattern for the markup and failure handling your application requires.
Adapt the pattern for other resources
The observer can watch a container or another element that represents a resource’s visibility. When the target intersects, your callback can apply the deferred resource—for example, by adding a CSS class that supplies a background image. The API reports intersection changes; your application decides what action to take. Keep the resource-specific loading and fallback logic separate from the visibility check.
For an ordinary image, prefer the native attribute unless a custom requirement justifies this extra JavaScript. Native loading delegates scheduling to the browser. An observer gives you more room to define application behavior, but also means you need to maintain that code and account for the markup and resource types you support.
Handle responsive images and changing page content
A page may use srcset and sizes to offer multiple image candidates. If you use a custom loader for responsive markup, defer and restore the complete responsive source information your page uses, not just one URL. Otherwise, assigning only src can bypass the candidate selection you intended. The minimal example above does not implement this case.
Similarly, the example observes elements found once at startup. If your application inserts more images later, those elements will not be included in that initial selection. Arrange for newly added elements to be observed as part of the code that inserts or renders them, or use a page architecture that applies the loading behavior when each image is created.
Plan how your UI responds if an image cannot load. The minimal observer stops observing after assigning the URL; it does not retry failed requests or display an error state. Add the behavior your interface needs rather than treating the example as a complete error-handling strategy.
Know when lazy images are actually loaded
Do not assume every lazy image has finished loading when the window’s load event fires. A browser may still have a lazy image pending at that point. If application code needs to know whether a particular image is ready, check its complete property or respond to that image’s load event.
Rank #4
const img = document.querySelector('#article-image');
if (img.complete) {
// The image has completed its loading process.
} else {
img.addEventListener('load', () => {
// The image is ready for code that depends on it.
}, { once: true });
}
Use an image-level check when the application depends on that image; a page-level event is not a guarantee that deferred content has been fetched.
Native loading or a JavaScript observer?
| Consideration | Native loading="lazy" |
Intersection Observer |
|---|---|---|
| Best fit | Ordinary off-screen <img> elements |
Custom visibility behavior or resources such as CSS background images and video posters |
| Who controls timing? | The browser, using its own calculated distance from the viewport | Your application decides what to do when an observed target intersects |
| Implementation overhead | A markup attribute; browser handles scheduling | JavaScript, observation setup, and resource-specific fallback and error handling |
| Responsive or dynamically added content | Works with your image markup; use the image’s regular responsive attributes as needed | Requires your code to account for responsive source data and elements added after initial setup |
Neither approach is established as universally faster for every page. The useful distinction is what you are loading and how much control the application needs. For ordinary images, browser-level lazy loading is broadly supported across major browsers; MDN marks HTMLImageElement.loading widely available since March 2022. MDN marks Intersection Observer widely available since March 2019. Check the exact browser versions in your support matrix if legacy compatibility matters.
Performance and reliability trade-offs
- Defer images users may never reach. Native lazy loading can reduce network and storage bandwidth for images that are not needed yet. It does not guarantee a fixed percentage improvement; the outcome depends on the page and which images visitors reach.
- Do not defer likely first-screen content. Keep the hero and likely Largest Contentful Paint candidate eager so the browser can discover important imagery early.
- Reserve layout space. Use dimensions or an equivalent aspect-ratio reservation to prevent image arrival from shifting surrounding content.
- Account for JavaScript-disabled browsing. Browsers that support native lazy loading defer images only when JavaScript is enabled, as an anti-tracking measure described in MDN’s image reference.
- Do not make startup code your only path for later content. An observer setup that queries the document once must also account for images added afterward.
Troubleshoot lazy loading
The image appears late or not at all
With native loading, the browser chooses when an image is close enough to fetch, so it may start before the image reaches the viewport. If an important image is delayed, remove loading="lazy" or set it to eager. With a custom observer, confirm the target is observed, it has the expected data-src, and your callback assigns a valid URL when it intersects.
The page jumps when an image appears
Reserve the image’s intrinsic width and height in markup, or preserve its correct aspect ratio in CSS. Recheck that the reserved ratio matches the file actually served; a mismatch can still change the layout.
Best Value
The image is missing after client-side rendering
The minimal observer selects matching elements only once. If the image was inserted after that code ran, it was not observed. Add observation when the new image is rendered, and verify its deferred URL attribute is populated.
Code runs at window.load, but the image is not ready
Lazy images may still be pending at the window load event. Check the target image’s complete property or attach a load listener to that image instead.
Responsive images use the wrong source
The minimal custom example assigns only src. If your markup relies on srcset or sizes, ensure the custom loading logic handles those attributes too; otherwise, prefer native loading for that ordinary image.
Or skip the browser setup
If the goal is to capture how a page looks rather than to change how your own page loads images, ScreenshotNeo offers a screenshot API and MCP server. A GET request can return an image or PDF, and its capture options include full-page screenshots with lazy images loaded. Its cleanup options address cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are not billed. AI agents can use its MCP server to take screenshots. The free plan includes 1,000 shots a month with no card, and paid plans start at $5 for 3,000 shots.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
JavaScript example:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for request details. To try it, sign up for ScreenshotNeo’s free plan.
Frequently Asked Questions
Does native image lazy loading need a JavaScript library?
No. For ordinary image elements, the HTML loading="lazy" attribute asks the browser to defer loading; a separate library is not required.
Can native lazy loading be used for CSS background images?
The loading attribute applies to image elements, not CSS background-image resources. Use custom visibility logic such as Intersection Observer if you need to defer a background image.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Free tools Windows power users keep installed
One-click scans. No signup required.




