skip to content

What does the loading="lazy" attribute on an HTML <img> do, and why is putting it on the large image at the top of the page a mistake?

level: middleimportance: must knowfreq 70%

answer

  1. deferred until near the viewport
  2. the decision needs layout first
  3. preload scanner never sees it
  4. hero image is already on screen
  5. eager above the fold, lazy below

basics

~20 s

loading="lazy" defers an image's download until layout shows it is near the viewport. On the hero image that delays the page's most visible paint, because the request cannot start until the browser has laid the page out.

solid answer

~40 s

`loading` on `<img>` takes two values: `eager`, which is the default, and `lazy`. A lazy image is not requested when the parser encounters it; the browser waits until layout places the element and decides it is within a proximity threshold of the viewport, then starts the fetch. That is exactly the wrong trade for an image the user already sees. Normally the preload scanner spots `src` early — even while a blocking stylesheet or script holds up the parser — and gets the download moving; `loading="lazy"` opts the image out of that head start and pushes it behind layout, which itself waits on CSS. So the practical rule is: never lazy-load anything in the first viewport, and lazy-load images below it. Pair either choice with `width` and `height` so the reserved box is correct.

code

html · 5 lines
html
<!-- Above the fold: let it fetch immediately -->
<img src="/img/hero.jpg" width="1600" height="900" alt="Team on stage at the launch event">

<!-- Far down the page: safe to defer -->
<img src="/img/gallery-7.jpg" width="800" height="600" loading="lazy" alt="Prototype enclosure, rear view">

go deeper

for a junior

Know the two values — eager is the default, lazy defers the download — and be able to say plainly that the big image at the top of the page should not be lazy.

for a middle

Explain the mechanism: a lazy image is skipped by the preload scanner and its request waits on layout, which waits on CSS. Contrast that with decoding, which is about presenting pixels, not fetching bytes.

for a senior

Show the production judgment: audit where a site-wide lazy default was applied, treat anything in the first viewport as eager, pair it with intrinsic dimensions, and describe how you would confirm the change in the network waterfall rather than by eye.

for a principal

Own the policy question — where the eager/lazy decision lives so a template or CMS default cannot silently lazy the hero, and how you keep that rule enforceable as components get reused in layouts nobody anticipated.

## What the attribute is `loading` is an attribute on `<img>` with two defined values: - `eager` — fetch the image as soon as the browser finds it. This is the default, so an `<img>` with no `loading` attribute is eager. - `lazy` — defer the fetch until the browser decides the image is needed. There is no `loading="auto"` and no `loading="none"`; writing one of those simply leaves the element at the default, eager behaviour. Native lazy loading is built into current browsers and needs no script. ```html <img src="hero.jpg" width="1600" height="900" alt="…"> <img src="footer-photo.jpg" width="800" height="600" loading="lazy" alt="…"> ``` ## What "deferred" actually means A lazy image is still parsed into the DOM immediately; only its network request is held back. The browser starts the request when layout tells it the element is within a proximity threshold of the viewport. That threshold is deliberately generous — browsers begin fetching some distance before the image scrolls into view, and the distance is typically larger on slow connections — so a well-behaved lazy image is usually already decoded by the time you scroll to it. It is a *scheduling* hint, not a promise that the image will never load. The key structural point is *when the decision can be made*. It requires layout, and layout requires the CSS to have arrived and been applied. So a lazy image's request is gated behind the render-blocking part of the page. ## Why the hero image is the wrong place for it Browsers run a lightweight **preload scanner** that reads ahead in the raw HTML while the main parser is blocked, and starts downloading resources it recognises — including `<img src>`. That is why an image declared near the top of a document often begins downloading before any script has run. `loading="lazy"` removes the image from that fast path: nothing is requested until layout says it is near the viewport, and by definition it *is* in the viewport, so all you have bought is a delay. The costs are asymmetric. Lazy-loading a below-the-fold image saves bytes for a user who never scrolls, and costs nothing visible if they do. Lazy-loading the image the user is staring at while judging whether the page has loaded costs you the paint you are most judged on and saves nothing at all — the image is going to be fetched either way. This is why "lazy-load all images" applied blindly, often via a template partial or a CMS default, is a classic regression: the site-wide default helpfully lazies the one image that must not be lazy. ## The rule to state in an interview Everything in the first viewport: leave `loading` off, or write `eager` explicitly to document the intent, and consider `fetchpriority="high"` on the single largest one. Everything below the first viewport: `loading="lazy"`. If you cannot tell where the fold is — responsive layouts, varying content lengths — err toward eager for the first image or two of the main content and lazy for the rest. ## Reserve the space either way A lazy image arrives late by design, so an unsized one leaves a collapsed box that expands when the file lands and shoves the content below it down. Always give `<img>` its intrinsic `width` and `height` attributes so the browser can reserve a correctly proportioned box before a single byte of the image arrives. ## Misreadings to avoid - **`loading` is not `decoding`.** `loading` decides *when the file is requested*; `decoding` only hints at when the already-downloaded pixels may be turned into a frame. - **Lazy loading does not reduce total bytes for a user who scrolls the whole page.** It changes ordering, and only saves data for content that is never reached. - **It is not a substitute for right-sized images.** A lazy 4 MB photo is still a 4 MB photo when it loads. - **Just-below-the-fold images can visibly pop in.** If a small scroll reveals them, treat them as above-the-fold. - **Lazy plus a very tall page is not free.** Each deferred image is a request that competes with whatever else is in flight when the user scrolls.

  • How does the browser decide it is time to start fetching a lazy image?
    It waits for layout, then compares the element's position against a proximity threshold — a distance ahead of the viewport rather than the viewport edge itself. Browsers deliberately start early, and typically use a larger distance on slower connections, so the image is often ready before it scrolls into view. It is a heuristic, not a spec-fixed number, so never assume a particular pixel margin.
  • Is there ever a case for lazy-loading an image that is inside the first viewport?
    Practically no. The fetch is going to happen regardless, so deferring it only delays the paint. The one adjacent case people confuse with this is content hidden behind interaction — a closed accordion or an off-screen carousel slide — which may be in the DOM but is never in the initial viewport, and is a fine candidate for `loading="lazy"`.
  • What breaks if a lazy image has no width and height attributes?
    The browser has no aspect ratio to work with, so it lays out a collapsed or default-sized box and then resizes it when the file arrives — pushing subsequent content down. Because a lazy image loads late by construction, the jump usually happens while the user is reading, which is the most disruptive moment for it. Set the intrinsic pixel `width` and `height` on every image.

saying these in an interview costs you the question

  • Says lazy loading always makes the page faster
  • Puts loading="lazy" on every image, hero included
  • Confuses loading with decoding, thinking lazy delays only rendering
  • Claims loading="lazy" needs an IntersectionObserver script in current browsers
  • Thinks the fetch starts only when the image touches the viewport edge
  • Believes lazy loading reduces total bytes for a user who scrolls the page

context