Fixing Images That Slow Down Page Load
Images are often the heaviest part of a page, but weight is only half the story.
What visitors feel is how images compete with the main content for the first few seconds of the load: a carousel downloading five slides before the headline appears, a hero photo hidden in CSS that the browser finds late, or dozens of thumbnails crowding the connection. This guide shows you how to measure the share of load time that images take on each template, decide which images deserve bandwidth first, and cut the rest down to a budget you can keep.
Four ways images cost load time
When a speed report blames images, it can mean several different things. Each one has a different fix, so it is worth separating them before changing anything.
| Cost | What happens | What it hurts |
|---|---|---|
| Bytes | Large files take a long time to download, especially on mobile data | Largest Contentful Paint if the big file is the main image; total load time and data use otherwise |
| Competition | Many images download at once and share the same bandwidth as the files that matter | The main image and critical files arrive later than they could |
| Late discovery | The browser only finds an image after reading CSS or running JavaScript | The main image starts downloading late, even if it is small |
| Decoding | Very large pixel dimensions take time and memory to decode and scale | Scrolling and responsiveness on low-end phones |
File size problems have their own detailed guides: oversized image files covers compression and metadata, image format covers AVIF and WebP, and lazy loading covers the loading attribute. This page sits above those. It is about the share of the load that images take, the order they arrive in, and which of the detailed fixes to apply first.
Measuring the image share of your page
Run the Page Speed Analyzer on each main template. It measures the page with real-user and lab data and lists the heaviest requests, along with total page weight and the number of requests. Note how many of the heaviest files are images and whether the element it names as slow is an image.
In Chrome DevTools
- Open the Network panel, tick Disable cache, choose a mobile throttling profile and reload.
- Click the Img filter. The status bar at the bottom shows the number of image requests and how much was transferred. Compare that with the unfiltered totals.
- Sort by the Waterfall column, so you can see which images start before the main content has finished loading.
A quick Console summary
After a fresh, uncached load, this snippet totals image bytes by who requested them, which separates HTML images from CSS backgrounds:
const imgs = performance.getEntriesByType('resource')
.filter(r => /\.(avif|webp|jpe?g|png|gif|svg)(\?|$)/i.test(r.name));
const byType = {};
imgs.forEach(r => {
byType[r.initiatorType] = (byType[r.initiatorType] || 0) + r.transferSize;
});
console.table(byType);
console.log(imgs.length + ' images, ' +
Math.round(imgs.reduce((a, r) => a + r.transferSize, 0) / 1024) + ' KB');
Images served from another domain may show a transfer size of zero unless that server sends a Timing-Allow-Origin
header. If your totals look too low, check the Network panel instead. The pattern-match on file extensions
will also miss images served from URLs without an extension, such as some image CDNs.
What to write down
- Total image bytes and image request count per template.
- How many images load before any scrolling happens.
- Whether the Largest Contentful Paint element is an image, and if so, when it starts downloading.
- Any image requested by CSS or JavaScript rather than by an
<img>tag.
The first few seconds: who gets the bandwidth
Early in the load, the connection is shared between the HTML, stylesheets, scripts, fonts and images. Modern HTTP/2 and HTTP/3 connections can carry many files at once, but they still share the same bandwidth. Browsers try to put important files first, and they generally start images at a low priority until they know which ones are visible. That guesswork is where things go wrong.
Carousels and sliders
A hero carousel with five slides often requests all five full-width images at the start, even though only one can be seen. The other four take bandwidth from the one that matters. Make only the first slide load eagerly, and load the rest lazily or once the carousel starts moving. Better still, ask whether the carousel is needed at all; a single strong image is usually faster and clearer.
Priority hints
The fetchpriority attribute lets you tell the browser which image matters. Put fetchpriority="high"
on the one image that is the main content of the first screen, and consider fetchpriority="low"
on images that are visible but unimportant, such as later carousel slides or small decorative graphics
near the top.
<img src="/img/hero-1200.webp" width="1200" height="600"
alt="Freshly baked sourdough on a wooden board"
fetchpriority="high">
<img src="/img/slide-2-1200.webp" width="1200" height="600"
alt="Our shop counter" loading="lazy" fetchpriority="low">
Use high on one image, or two at most. Marking everything high is the same as marking nothing.
Icons and small graphics
A header with a dozen small PNG icons makes a dozen requests. Each is tiny, but together they add overhead and compete with the main image. Replace them with inline SVG or an SVG sprite, or with an icon font you already load, and the requests disappear.
Images the browser finds too late
The browser's preload scanner reads the HTML ahead of the parser and starts downloading images it
can see in <img> tags straight away. It cannot see images that only exist in other
places:
- CSS backgrounds. A hero set with
background-imageis only requested once the stylesheet has loaded and the browser has worked out that the element needs it. - JavaScript-inserted images. Slider and gallery scripts that build images from a
data attribute, such as
data-src, wait for the script to download and run. - Images inside components rendered on the client. In a single-page app, nothing is requested until the framework has built that part of the page.
If the late image is the main content of the first screen, the fix is to put it in the HTML as a real
<img> element, styled to cover its area with object-fit: cover. That
usually looks identical to a background and loads much earlier. If you cannot change the markup, add
a preload in the head:
<link rel="preload" as="image"
href="/img/hero-1200.webp"
imagesrcset="/img/hero-800.webp 800w, /img/hero-1200.webp 1200w, /img/hero-1800.webp 1800w"
imagesizes="100vw"
fetchpriority="high">
The imagesrcset and imagesizes attributes should match the image the page
actually uses, otherwise the browser downloads one version for the preload and another for the page.
The fix, step by step
Step 1
Measure the image share of each template
Test your home page, a listing or category page, a product or service page and an article on a throttled mobile profile with the cache off. For each, note total image bytes, the number of image requests, the total page weight and whether the slowest element is an image. Templates where images make up most of the bytes, or where dozens of images load before scrolling, are where this work will pay off first, so start there rather than with the home page by habit.
Step 2
Rank images by when they load, not by size
Sort the Network panel by start time and look only at images that begin downloading before the main content has appeared. Those are the ones competing with the page's critical files. A heavy image far down the page that already loads lazily matters much less than a modest one that loads early for no reason. Mark each early image as needed on the first screen, visible but unimportant, or not visible at all without scrolling.
Step 3
Remove first-screen images that do no work
Go through the early images and ask what each one does for the visitor. Hidden carousel slides, a second logo for the mobile menu, decorative flourishes, social icons as separate PNG files and background textures are common candidates. Remove them, switch icons to inline SVG, or load extra slides only when the carousel moves. Every image you take out of the first seconds of the load gives more bandwidth to the ones that remain and costs nothing to maintain.
Step 4
Give the main image priority and demote the rest
Find the single image that forms the main content of the first screen and make
sure it is not lazy-loaded. Add fetchpriority="high" to it. Give later carousel slides and
minor visible graphics fetchpriority="low", and add loading="lazy" to everything
below the first screen. Test again and check in the waterfall that the main image now starts near the
top of the image requests rather than somewhere in the middle of them.
Step 5
Move late-found images into the HTML
If the main image is a CSS background or is inserted by a script, change the template so
it is a normal <img> element with object-fit: cover, which the browser
can find while reading the HTML. Where the markup cannot change, add a preload link in the
head with matching imagesrcset and imagesizes values. Then check the waterfall
again to confirm that the image now starts downloading alongside the stylesheets rather than after them.
Step 6
Match pixel dimensions to the display size
Compare each image's natural width with the width it is shown at on a phone and on a desktop.
A 3000-pixel photo shown in a 400-pixel card wastes most of its bytes. Generate a few widths of each
image and list them in srcset, with a sizes value that describes the real layout
width, so each device picks a suitable file. Most content systems and image CDNs can create the widths
automatically once you set them up.
Step 7
Cache images so repeat views cost nothing
Check the response headers of a few images. Images whose file names change when the picture
changes, such as names containing a version or hash, can be sent with Cache-Control: public, max-age=31536000, immutable,
so a returning visitor or someone browsing a second page downloads nothing. Images whose names stay the
same when replaced need a shorter lifetime. If your host sends no caching headers at all, fix that at
the server or CDN level.
Step 8
Set an image budget per template
Using your new measurements, write down a limit for each template, such as a maximum for total image bytes on first load and a maximum number of images before scrolling. Share it with whoever uploads content and whoever edits the theme. Check it whenever a template changes or a new section is added, and retest the templates monthly. A budget turns a one-off clean-up into a standard that stops the page from gradually getting heavy again.
Decoding, huge dimensions and animation
Downloading is not the end of an image's cost. The browser also has to decode it into pixels in memory, and scale it to the size it is shown at. For normal photos this is quick, but some cases are expensive on low-end phones:
- Very large pixel dimensions. A well-compressed image can be small in bytes but very large in pixels, and decoding cost follows the pixel count. Resizing the original fixes both.
- Many large images in a grid. Product grids and galleries with full-size originals can make scrolling feel sticky while images decode.
- Animated GIFs. Long animations as GIFs are heavy to download and decode. A short muted, looping video file is usually far smaller. The image format guide covers converting them.
The decoding="async" attribute hints that the browser may decode an image without holding
up other rendering work. It is harmless on most images below the first screen, but it does not reduce
the total work, so it is no substitute for sending sensibly sized files.
Platform notes
WordPress
WordPress generates several sizes of each upload and adds srcset automatically for images
inserted through the editor. Recent versions also add fetchpriority="high" to an image they
judge to be the main one and lazy-load others, but themes and page builders can override this, so check
the output. Hero images set as section backgrounds in page builders are a very common source of late
discovery. See WordPress images
for the details.
Shopify
Shopify's image CDN can resize and convert images on request, so most speed gains come from the theme asking for sensible widths and marking the first product or banner image correctly. Shopify images covers the Liquid involved.
Next.js
The built-in image component generates sizes, lazy-loads by default and supports a priority setting
for the main image, but it relies on you giving an accurate sizes value. See Next.js
images.
Custom and static sites
You will need a build step or an image CDN to produce several widths and modern formats. The image CDN guide compares the options.
Edge cases
- The main content is text, not an image. If the slowest element is a heading, image work will not help that metric much. Look at fonts, render-blocking files and server response instead, and treat images as a page-weight issue.
- Different main images on mobile and desktop. If the layout uses one image on phones
and another on desktop, use a
<picture>element with media conditions so only one is downloaded, and set priority on that element's image. - Third-party images. Review widgets, social feeds and ad slots can load many images you do not control. Load those widgets late or on interaction.
- Infinite scroll and long listings. Make sure new batches of images load lazily and that pagination does not bring in far more images than a visitor will see.
- Images needed for sharing and search. Changes to lazy loading and markup should not hide the main image from search engines or social previews; check the rendered HTML after you make them.
Keeping image cost under control
Images creep back because new content is added every week by people who never see a waterfall. The budget from step 8 is the main defence, backed by a few settings:
- Set a maximum upload size in your content system, or resize automatically on upload.
- Give content editors a short note: the widths to export at, the formats to use and when not to use a carousel.
- Re-run the analyzer on your main templates after design changes, and compare image bytes and request counts with your last result.
The Image Optimisation Guide explains the wider set of image checks, and the LCP guide goes deeper on the main image once you have cleared the competition around it.
1. How do I know if images are what makes my page slow?
Load the page with the cache disabled and filter the Network panel to images. Compare image bytes and requests with the page totals, and check whether the largest element on the first screen is an image. If images make up most of the bytes, or the main image starts downloading late, images are a major cause. If the page is slow before any image starts, look at server response and render-blocking files first, because those delay everything, images included.
2. Should the main hero image be lazy-loaded?
No. Lazy loading tells the browser to wait until an image is near the viewport, and the browser can only work that out after layout. For an image that is already on the first screen, that wait simply delays it. Load the main image normally and consider adding fetchpriority high to it. Use lazy loading for images further down the page, where it saves bandwidth for the content visitors see first without them noticing any delay.
3. What does fetchpriority do, and is it safe to use?
It is an HTML attribute that tells the browser how important a resource is compared with others of the same type. Setting it to high on the main image asks the browser to fetch it sooner; setting it to low on unimportant images lets them wait. Browsers that do not support it simply ignore it, so it is safe to add. The main risk is overuse: if many images are marked high, the hint loses its value.
4. Why is my background image loading so late?
Because the browser does not know it exists until it has downloaded the stylesheet, built the page structure and worked out that an element uses that background. An image in an img tag is found almost as soon as the HTML arrives. If the background is the main visual on the first screen, change it to an img element that covers the area, or add a preload link in the head so the browser can start fetching it early.
5. How many images is too many on one page?
There is no fixed number. What matters is how many load before the visitor scrolls, and how many bytes they add up to on a phone. A long article with thirty lazily loaded images below the first screen can be fast, while a home page that loads eight slider images and a dozen icons up front can be slow. Count images that load without scrolling on each template and set your own limit based on what you measure.
6. Do WebP or AVIF fix slow images on their own?
They help, because they usually produce smaller files than JPEG or PNG at similar quality. They do not fix images that are far larger in pixels than needed, images that load before they are needed, or a main image that the browser finds late. Converting format is one of several fixes. Start with which images load and when, then reduce pixel dimensions, then change format, and you will get more from each step than from format alone.
7. Does image caching help first-time visitors?
Not directly. Browser caching only helps once a visitor has already downloaded an image, so it speeds up later pages and return visits. For first-time visitors, the gains come from sending fewer and smaller images and loading the important one first. A content delivery network does help first-time visitors, because it stores copies close to them and can resize images on the fly, which shortens the download even on the very first view.
The Page Speed Analyzer measures your page with real-user and lab data, reports total page weight and requests, and names the heaviest files holding the page back, so you can see which images to deal with first.
Open Page Speed Fixes โ