2HAASStart a project
All posts

Tech Tips

Native image lazy loading: one attribute, used well

The loading attribute delays offscreen images until the user scrolls near them. It takes one word, but on the wrong image it makes a page slower. Where it belongs.

The 2HAAS team2 min read

A long page can hold dozens of images, and most visitors never scroll to the bottom. Without lazy loading, the browser downloads all of them anyway. They compete with the content the visitor is looking at, and on mobile they spend data the visitor pays for.

Browsers can defer those images themselves. No library is needed.

The attribute

<img
  src="/img/team.jpg"
  alt="The team at the Dhaka office"
  width="1200"
  height="800"
  loading="lazy"
/>

With loading="lazy", the browser waits until the image is close to the viewport before requesting it. Every current browser supports it. A very old browser ignores the attribute and loads the image normally, so nothing breaks and no JavaScript fallback is required.

Always set width and height

A deferred image has no size until it arrives, unless you state one. Without width and height, the text below jumps when the image appears. That is a layout shift, and it counts against your Core Web Vitals.

With both attributes, the browser reserves the right space from the start. The image can still be fluid in CSS:

img {
  max-width: 100%;
  height: auto;
}

Do not lazy load what is visible first

This is the common mistake. Adding the attribute to every image, including the hero image, makes the page slower. The browser holds back the most important image until layout is done, and the Largest Contentful Paint suffers.

Images at the top of the page should load immediately. Mark the main one as important:

<img src="/img/hero.jpg" alt="…" width="1600" height="900" fetchpriority="high" />

A simple rule: the hero image and anything visible without scrolling loads eagerly. Everything below that is lazy.

It works for iframes too

<iframe
  src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
  title="Product demo"
  loading="lazy"
  width="560"
  height="315"
></iframe>

Embedded videos and maps are heavy. Deferring them often saves more than all the images together.

What it does not cover

CSS background images are not affected by the attribute. For those, and for anything that needs code to run when it becomes visible, use the Intersection Observer API.

Check that it works

Open the Network panel in DevTools, filter by images and reload. Only the images near the top should load. Scroll down and watch the rest arrive as you go.

Summary

Image Attribute
Hero or main image fetchpriority="high", no lazy loading
Other images visible on load Default loading
Everything below the fold loading="lazy"
All images width and height

Lazy loading decides when an image loads. How heavy it is matters as much. See WebP and AVIF and the full guide to image optimization for the web.

Keep reading

  1. Tech Tips2 min read

    WebP and AVIF: modern image formats with a fallback

    WebP and AVIF produce much smaller files than JPEG and PNG at the same visual quality. How to convert your images and serve them with the picture element.

  2. Tech Tips2 min read

    Client-side caching for API responses

    Refetching the same data on every visit wastes time and bandwidth. HTTP cache headers, an in-memory cache and browser storage each solve a different part of the problem.

  3. Tech Tips2 min read

    Tree shaking: why your bundle still ships unused code

    Tree shaking removes code you never import, but only when the bundler can prove it is unused. ES modules, the sideEffects flag and your import style decide what goes.

Tell us what’s in the way.

A few lines is enough. You get a candid yes, a candid no, or a sharper question.

  • Working software in week four
  • A reply in one business day
  • Handover is the deliverable

Prefer email? [email protected]

The problem, who it is for, and what is in the way right now.

One business day to a real reply. No sales sequence, no call required.