What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Add loading="lazy" to images that are not needed for the initial view, and include dimensions so the browser can reserve their space. The browser then decides how far from the viewport to begin fetching each image.

Use the native HTML attribute first

For ordinary below-the-fold images, the simplest and most widely supported implementation is the native loading attribute:

<img
  src="photo.jpg"
  alt="Description of the image"
  width="800"
  height="600"
  loading="eager"
>

The loading="lazy" value is a browser hint, not an exact scroll trigger. The browser calculates a distance from the viewport and may start the request before the image is visible. The exact threshold can vary with browser decisions, connection conditions and other factors. See the MDN <img> reference.

What each attribute does

  • src identifies the image resource.
  • alt provides an accessible text alternative. Use meaningful text for informative images and alt="" for purely decorative images.
  • width and height establish the intrinsic aspect ratio before the file arrives.
  • loading="lazy" asks the browser to defer a non-critical download.

Reserve space before a lazy image loads

Always provide intrinsic dimensions or another definite aspect ratio. Without dimensions, an unloaded image can have zero width and height. That can cause layout shifts and, as MDN notes, can prevent a lazy image from intersecting the visible area as expected. If the source is responsive, keep the ratio consistent with the files you serve.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<figure>
  <img
    src="team-800.jpg"
    alt="The support team in the office"
    width="800"
    height="533"
    loading="eager"
  >
  <figcaption>Our support team</figcaption>
</figure>

If the image is displayed in a differently shaped box, use CSS or an aspect-ratio rule to reserve the intended shape. The important requirement is that the layout has a known size before the request completes.

Decide which images should be lazy

Keep immediately needed images eager

Images needed for the initial view, such as a prominent hero image or a product image visible on first render, are usually best left with the default eager behavior. Do not apply lazy loading to every image automatically. If a critical image is discovered late, investigate prioritization separately and measure the result. The fetchpriority attribute is a separate hint; it does not replace the decision about whether an image is critical.

<img
  src="hero.jpg"
  alt="Dashboard overview"
  width="1440"
  height="900"
  fetchpriority="high"
>

loading is omitted here, so the image remains eager. Use a priority hint only when you have a measured reason to influence fetching order.

Defer images below the initial view

Gallery items, article illustrations farther down a page, avatars in a long list and other images a visitor may never reach are good candidates for loading="lazy". Deferring them can avoid fetching assets that a reader never views and reduce initial network work. The size of the benefit depends on image count, file sizes, layout, connection and how far the reader scrolls; there is no universal speed percentage.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Combine lazy loading with responsive images

Lazy loading controls when a candidate image is requested. srcset and sizes control which candidate is appropriate for the rendered width.

<img
  src="photo-800.jpg"
  srcset="photo-400.jpg 400w,
          photo-800.jpg 800w,
          photo-1600.jpg 1600w"
  sizes="(max-width: 800px) 100vw, 800px"
  alt="Mountain trail at sunrise"
  width="1600"
  height="1067"
  loading="eager"
>

sizes="auto" is supported together with loading="lazy"; it lets the browser use the expected rendered width for a lazy image. The remaining media condition and length provide a fallback for implementations that do not understand auto. Keep explicit width and height so the aspect ratio is still reserved.

Understand the JavaScript requirement

Native image deferral is conditioned on JavaScript being enabled. MDN documents this behavior as an anti-tracking measure. A page should therefore remain usable when scripting is unavailable: keep a real src, useful alternative text and dimensions in the markup instead of relying on a script to create the image request.

When custom JavaScript is justified

Use native loading for standard images. A custom solution is appropriate only when you need behavior the attribute does not provide, such as a special visibility rule, a legacy compatibility strategy or coordination with an application’s state. The usual modern primitive is Intersection Observer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Intersection Observer example

This complete example stores the URL in data-src and assigns it when the image approaches the viewport. It includes dimensions and a fallback for a browser without Intersection Observer.

<img
  class="js-lazy"
  data-itgw-was-lazy-src="photo.jpg"
  alt="Description of the image"
  width="800"
  height="600"
>

<script>
(() => {
  const images = document.querySelectorAll('img.js-lazy[data-src]');

  const load = (image) => {
    image.src = image.dataset.src;
    image.removeAttribute('data-src');
  };

  if (!('IntersectionObserver' in window)) {
    images.forEach(load);
    return;
  }

  const observer = new IntersectionObserver((entries, obs) => {
    entries.forEach((entry) => {
      if (!entry.isIntersecting) return;
      load(entry.target);
      obs.unobserve(entry.target);
    });
  });

  images.forEach((image) => observer.observe(image));
})();
</script>

The fallback above loads the images immediately rather than risking that they never appear. Older implementations can use scroll and resize handlers instead, but those handlers require careful throttling and cleanup. Adding a library without a concrete compatibility or behavior requirement usually adds complexity without improving the native case.

Test that the implementation behaves as intended

  1. Put one eager image and one lazy image on a page, each with explicit dimensions.
  2. Open the page with JavaScript enabled and inspect the image requests while the lazy image is well below the viewport.
  3. Scroll toward the lazy image and observe when its request begins. Do not expect the request to start at the exact pixel where it becomes visible; the browser chooses the threshold.
  4. Test a long page where a visitor stops before the image. Deferral can prevent that asset from being fetched at all.
  5. For application code that needs to know about an individual image, inspect that element’s complete property and interpret it for your use case. The window load event is not a guarantee that every lazy image has finished loading.
  6. Test with scripting disabled as well. Native lazy loading is conditioned on JavaScript, so verify that your page still has understandable markup and does not depend on a script-only src.

Common problems and fixes

The image still loads immediately

Check that the image is actually outside the initial view, that the attribute is spelled loading="lazy", and that the browser is not choosing an early threshold. Lazy loading is a hint, so an early request is not necessarily a failure.

The page jumps when images appear

Add accurate width and height values or establish an equivalent aspect ratio. Incorrect dimensions can reserve the wrong shape; missing dimensions reserve none.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

A lazy image never appears

Confirm that it has a usable src in the native approach, or that your script assigns data-src correctly in the custom approach. Ensure the element has non-zero dimensions and can intersect the viewport. MDN specifically warns that an unloaded image with zero dimensions may never intersect a visible part of an element.

The responsive image is blurry or too large

Review the width descriptors in srcset and the rendered-width hints in sizes. Keep a fallback after sizes="auto" for older implementations and ensure each candidate’s dimensions match its actual file.

Analytics or code runs before every image is ready

Do not use the window load event as proof that all lazy images are complete. Track the individual image state your application needs, using the element’s complete property with an interpretation appropriate to that workflow.

The custom observer increases complexity

Remove it unless you need custom visibility behavior or a compatibility requirement. Native loading="lazy" is the platform feature intended for ordinary image markup.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and accessibility notes

  • Lazy loading reduces initial work only when there are off-screen images that may not be viewed. It is not a guaranteed improvement for every page.
  • Image dimensions protect visual stability and help the browser reason about intersection.
  • Alternative text remains an accessibility requirement; lazy loading does not change whether an image conveys information.
  • The browser owns the loading distance and timing. Application code should tolerate an image arriving earlier or later than expected.
  • Native support has been broadly available since March 2022, according to MDN’s compatibility documentation; still test the browsers and embedded webviews your audience actually uses.

Or skip the browser setup

If you need a rendered capture of a page to check its lazy-loaded images, ScreenshotNeo provides a website screenshot API and MCP server. It can capture a URL without you maintaining browser automation. Cookie and consent banners, newsletter popups and chat widgets are removed before the shot; bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP tools—take_screenshot, get_page_info and capture_pdf—can be used by Claude, Cursor and other MCP clients.

One GET request returns PNG, JPEG, WebP or PDF. The API also supports full-page captures with lazy images loaded, element selection, device and viewport settings, retina scale, custom CSS and JavaScript, click-before-capture actions, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture and a usage API.

cURL

See the ScreenshotNeo documentation for parameter details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every feature is included on every plan. The Free plan provides 1,000 shots per month with no card; paid plans are Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000) and Business ($249 for 1,000,000). Yearly billing gives two months free. Create a free ScreenshotNeo account to get the 1,000 monthly shots without entering a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.