Most image optimization advice focuses on file size: compress more, use WebP, reduce dimensions. This is necessary and correct. But file size isn’t the only variable that determines how fast images reach users.
Two pages can have identical total image payload sizes and wildly different performance scores, because one loads images in an order that serves the user’s perception first, and the other doesn’t.
How Browsers Load Images
When the browser parses an HTML document, it builds a list of resources to fetch—CSS, JavaScript, fonts, images. It assigns these resources priorities based on their relationship to what the user currently sees.
Images in the initial viewport get higher priority than images below the fold. Images in <link rel="preload"> tags get very high priority. Images with loading="lazy" get low priority and don’t load until they’re close to the viewport.
The challenge: the browser doesn’t always know which image matters most without hints from the developer. And even with correct prioritization, several common mistakes cause low-priority resources to block high-priority images.
The LCP Image Problem
Largest Contentful Paint (LCP) measures when the largest visible element in the viewport finishes loading. For most websites, that’s the hero image. Google uses LCP as a Core Web Vitals metric.
A slow LCP means users see an incomplete page—typically a blank space where the hero image belongs—while the browser is still loading other resources. Everything else on the page can be instantaneous; if the hero image is slow, the page feels slow.
File size is one factor. But a 100KB hero image can still produce a slow LCP if:
-
It’s discovered late. The browser can’t start loading an image until it knows the image exists. If the hero image is loaded via CSS (
background-image) or injected by JavaScript, the browser can’t discover it until the CSS or JS is parsed and executed—which may be seconds after the HTML arrives. An<img>tag in the HTML is discovered immediately. -
It competes with higher-priority blocking resources. Render-blocking CSS or JavaScript in the
<head>delays everything. While the browser is blocked executing a synchronous script, your hero image is waiting. -
Missing
fetchpriority="high"on the most important image. By default, browsers treat all images with similar priority. Explicitly telling the browser which image is most important viafetchpriority="high"on the hero image<img>tag causes it to be fetched before other images. This is a single attribute that can meaningfully improve LCP scores without changing file size.
The Lazy Loading Anti-Pattern
loading="lazy" is excellent—for images below the fold. A common mistake is applying it globally, including to above-the-fold images.
When loading="lazy" is on the hero image:
- The browser defers loading until the image is near the viewport
- “Near the viewport” for the initial page load means the image is technically already in the viewport
- But the deferral introduces a delay before loading even starts
- LCP scores suffer
The correct pattern:
- Hero image and any content visible on initial load: no lazy loading (or explicitly
loading="eager") - Everything below the fold:
loading="lazy"
This sounds obvious, but it breaks in practice when:
- A CSS framework applies lazy loading globally to all images
- A WordPress or CMS plugin enables lazy loading for “all images” as a performance optimization
- A developer copies the
loading="lazy"attribute without thinking about placement
Preloading Critical Images
For hero images—especially those delivered from a CDN or image transformation service where the URL may be dynamic—a <link rel="preload"> in the <head> tells the browser to start fetching the image before the HTML is fully parsed:
<link
rel="preload"
as="image"
href="/images/hero.webp"
imagesrcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w, /images/hero-1200.webp 1200w"
imagesizes="100vw"
/>
This is especially valuable for hero images loaded via <picture> with multiple format options, where the browser needs to evaluate srcset before it can start downloading. A preload link bypasses this evaluation step.
Don’t preload more than one or two images—preloading too many resources competes with itself and other critical assets.
Below-the-Fold Images Loading Too Early
The inverse problem also exists. Pages with many images that are all loading eagerly (even images 1,500px below the fold) block bandwidth that should be going to LCP and initial-viewport content.
loading="lazy" on below-fold images defers them until they’re near the viewport. Bandwidth that would have gone to those images is now available for visible content, which loads faster.
For image-heavy pages (photography galleries, product listings, long articles with many inline images), lazy loading below-fold images can reduce initial page weight significantly without any change to actual file sizes.
Responsive Images and Wasted Downloads
Another loading-order adjacent problem: serving large images to small screens. If your <img> tag doesn’t use srcset and sizes, the browser downloads the largest version of every image on every device.
A user on a 390px-wide phone downloading a 2400px hero image is downloading ~6× the pixels they need. With srcset:
<img
src="/images/hero-800.jpg"
srcset="/images/hero-400.jpg 400w, /images/hero-800.jpg 800w, /images/hero-1200.jpg 1200w"
sizes="100vw"
alt="Hero image"
/>
The browser picks the right size. A 390px phone downloads the 400w version; a 1440px desktop downloads the 1200w version. This reduces actual bytes downloaded without changing the visual quality.
The Actual Priority Order
For a typical landing page, the correct image loading strategy is:
- Preload the hero image (if it’s in a
<picture>or delivered from a CDN with variable URLs) fetchpriority="high"on the hero<img>element- No lazy loading on any image in the initial viewport
loading="lazy"on all images below the fold- Correct
srcsetandsizeson all images to avoid oversized downloads on small viewports
These five steps cost nothing in additional download time and often produce LCP improvements of 500ms–2s on real devices—more than most compression optimizations deliver on their own.
File Size Still Matters
None of this replaces compression. A 3MB hero image with fetchpriority="high" still loads slowly on a mobile connection. The loading order optimizations make the most of whatever payload you’re serving—they don’t substitute for keeping that payload small.
The correct mental model: compress images to the smallest size that looks good, then apply loading order best practices to serve visible content first. Both layers of optimization are necessary; neither alone is sufficient.
Start with the payload: compress your images with the free Image Compressor, then apply loading order best practices in your HTML.