glimfly
web updated

What is lazy loading (and why did my images stop appearing)?

In one sentence:Lazy loading defers fetching an image, iframe, or chunk of code until it's actually needed, usually because the user is about to scroll it into view or open the screen that uses it.

What it actually is

Lazy loading means the browser waits to fetch something until it’s actually about to be needed, instead of grabbing everything the moment the page opens. It applies to two different things that often get lumped together: images (and iframes, video) that stay off-screen until the user scrolls near them, and JavaScript that stays undownloaded until a route or feature is actually used.

For images, you turn it on with one attribute: <img loading="lazy" src="photo.jpg">. The browser calculates a rough distance from the visible screen and only starts the fetch once the image gets close. For code, the tool is a dynamic import(): instead of bundling a feature’s JavaScript into the file that loads on every visit, your build tool splits it into its own chunk and fetches that chunk only when the user opens the screen that needs it.

Think of it like a restaurant that preps only the dishes on the current table’s order, not the whole menu, before anyone sits down. Lazy loading works the same way: fetch what’s in front of the user now, and fetch the rest only once they ask for it. It’s a separate lever from browser caching: lazy loading decides when a request happens on this visit, while the cache decides whether a repeat visit needs to make that request at all.

Why your AI just did this

Your agent adds loading="lazy" to images because it’s a real, well-documented fix for slow pages loaded with photos, and it’s a one-word change: no library, no configuration, just an HTML attribute the browser has supported for years. If your app got slow and you told your agent that, “make images lazy load” is one of the first moves it reaches for, especially on a page with a long feed or gallery.

For code, Cursor, Claude Code, or Lovable reach for dynamic import() (React’s lazy() plus Suspense, or next/dynamic in Next.js) when a component is large but rarely used right away: a chart library, a modal that opens on click, an admin panel most visitors never open. Splitting it out means the page’s first load skips code the visitor might never touch.

Both moves come from the same instinct: your agent noticed the app shipping more than the current screen needs, and cut the parts that can wait.

When you’ll run into it

You’ll usually meet lazy loading in one of two moods. The good one: your agent mentions it after you complain the app “feels slow,” and images further down a page stop loading until you scroll near them. Open your browser’s Network tab (F12, or Cmd+Option+I on Mac) and you’ll see image requests firing in waves as you scroll, instead of all at once on page load.

The bad one: your hero image, or any image near the top of the page, doesn’t appear immediately, or a Lighthouse/PageSpeed report flags “Largest Contentful Paint image was lazily loaded.” That happens because a lazy-loaded image inside the visible screen still waits for the browser to finish downloading and applying CSS before it requests the image at all, rather than starting the fetch the instant the browser reads the <img> tag in the raw HTML. For your biggest, most-visible image, that wait directly delays how fast the page feels loaded.

On the code side, you’ll see it as a brief flash of a loading spinner or skeleton screen the first time you open a particular screen, because that screen’s JavaScript is still downloading. That’s expected: it’s the tradeoff for a faster initial load everywhere else.

What to check

  • Never put loading="lazy" on your hero image, banner, or anything likely to be the largest thing visible when the page first loads. Leave those on the default (eager) behavior
  • If a performance report flags “LCP image was lazily loaded,” find that specific image and remove the attribute rather than lazy-loading fewer images across the board
  • For images below the fold (later in a feed, a gallery, footer content), loading="lazy" is exactly right and needs no further tuning
  • For code splitting, confirm the split component isn’t something the user needs instantly, like the main layout or above-the-fold content. Splitting those adds a delay for no benefit
  • Check that lazy-loaded images have explicit width and height (or a CSS aspect ratio) set, so the page doesn’t jump around as each image finishes loading
  • Pair lazy loading with a CDN for the images themselves if you have many or large ones; lazy loading cuts how many requests fire at once, a CDN cuts how long each one takes
  • Open your Network tab and watch what loads on first paint versus what loads later. If something critical is missing from that first wave, it’s a sign it got lazy-loaded by mistake

Say it like a dev

Instead of: “the images take forever to show up” Say: “Images below the fold can stay loading=lazy, but the hero image at the top should load eagerly since it’s probably my LCP element.”

Instead of: “the app is slow to load, can you lazy load stuff” Say: “Can you code-split the admin dashboard with a dynamic import so it doesn’t add to the bundle everyone downloads on the homepage?”

Instead of: “my Lighthouse score for LCP is bad, just try things” Say: “Lighthouse says my LCP image was lazily loaded. Can you find that image and remove loading=lazy from it specifically?”

People actually ask

“Should every image on my site be loading=lazy?”

No. Images visible when the page first loads, especially your hero image or anything likely to be the Largest Contentful Paint, should load eagerly (the default). Lazy loading only helps for images below the fold, because it lets the browser skip fetching them until the user scrolls near them.

“My agent added loading=lazy and now my Lighthouse score got worse. Why?”

A lazy-loaded image inside the visible viewport waits for the browser to parse the page's CSS before it even starts fetching, instead of being requested the instant the browser finds the tag in the HTML. If that image is your largest visible element, delaying its fetch directly delays your LCP score. Removing loading=lazy from that one image, or from any image near the top of the page, usually fixes it.

“Is lazy loading the same thing as code splitting?”

They're related but not the same. Lazy loading usually refers to media (images, iframes, video) that the browser defers with the loading=lazy attribute. Code splitting defers JavaScript itself, using a dynamic import() so a chunk of your app only downloads when a route or feature is actually used. Both exist to avoid loading things nobody asked for yet.

Related terms

Checked against

free tool · no signup

Just met this in a real session? Glim reads it in plain words.

Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.

Explain my session →