Image optimization means serving every image in the right format (AVIF or WebP for photos, SVG for logos and icons), at roughly the size it is displayed and properly compressed. srcset and sizes let the browser pick the right file, width and height prevent layout shift, and lazy loading defers offscreen images, but never the LCP image.
On most business websites, images are the heaviest part of the page. This article is not about which photo to choose; it is about how the chosen image reaches the browser: format, markup, loading priority and automation in the build pipeline.
What is the difference between WebP, AVIF, JPEG and PNG?
WebP and AVIF are modern image formats that produce noticeably smaller files than JPEG and PNG at the same visual quality. AVIF usually beats WebP on file size but is slower to encode. As of 2026, current versions of Chrome, Edge, Firefox and Safari all support both WebP and AVIF.
- —JPEG: Universal support, fine for photos, no transparency. Larger than modern formats at equal quality.
- —PNG: Lossless with transparency; good for screenshots and sharp-edged graphics, usually far too heavy for photos.
- —WebP: Lossy and lossless modes, transparency and animation; fast to encode, a safe default.
- —AVIF: Often the smallest file for photos, supports HDR and wide colour gamut; slow to encode, so generate it at build time.
- —SVG: Vector format for logos, icons and illustrations; sharp at any resolution and usually just a few KB.
A practical setup: generate AVIF for photos, offer WebP as a fallback and keep a JPEG for very old browsers. You do this with a picture element containing source lines with a type attribute; the browser uses the first format it supports. As starting points, quality 75-80 for WebP and 50-60 for AVIF work for many photos, but decide by comparing visually.
Related services
How do you serve responsive images with srcset and sizes?
Responsive images work by listing several widths of the same image in the srcset attribute and telling the browser, through sizes, how wide the image will be on the page. The browser factors in viewport width and pixel density and downloads the most suitable file, so phones never receive desktop-sized images.
- —Use width descriptors: for example, generate four files at 480w, 800w, 1200w and 1600w.
- —Write sizes from the real layout: for full width on mobile and 800 pixels on desktop, sizes="(max-width: 768px) 100vw, 800px".
- —Without sizes, the browser assumes 100vw and may download a much larger file than needed.
- —For small fixed-size images (avatars, logos), 1x and 2x density descriptors are enough.
- —If mobile and desktop need different crops (art direction), use source lines with media conditions inside a picture element.
A common mistake is showing a 2400-pixel original inside a 400-pixel card. Even for high-density screens, twice the displayed width is enough in most cases; anything beyond that only burns bandwidth.
Why do width and height attributes prevent CLS?
Adding width and height to an image tag lets the browser calculate the aspect ratio and reserve space before the image has downloaded. When the image arrives, the text below it does not jump, so Cumulative Layout Shift (CLS) stays low. Combined with width: 100% and height: auto in CSS, the image remains fully responsive.
The values should reflect the file's real proportions, not the displayed size; the browser only uses the ratio. For background images and embeds, the CSS aspect-ratio property does the same job. Late-loading areas such as comment widgets or ads also need reserved height.
Read next
Where should lazy loading be used, and where not?
Lazy loading defers the download of offscreen images until the user scrolls near them, and browsers support it natively through the loading="lazy" attribute. Lazy loading belongs on images below the first screen; it must never be applied to the Largest Contentful Paint (LCP) element, such as the hero image.
- —Apply it to: galleries lower on the page, blog cards, footer logos and images inside tabs.
- —Do not apply it to: the hero image, the first product photo, the logo or anything that could be the LCP element.
- —Put the LCP image in an img tag rather than a CSS background so the browser discovers it early.
- —decoding="async" can reduce the main-thread cost of decoding large images.
What does fetchpriority="high" do?
fetchpriority="high" tells the browser to fetch an image ahead of other resources. Browsers start images at low priority by default, so adding this attribute to the LCP image can shorten LCP time. Use it on one or two images per page at most; giving everything high priority makes prioritisation meaningless.
How do you compress images automatically at build time?
Image compression is most reliable when it is automated at build time: high-resolution originals in a source folder are turned into AVIF, WebP and JPEG files at several widths by a script or bundler plugin. Nobody adding content has to compress files by hand, and no unoptimized image ever reaches production.
- —sharp: A fast Node.js image library, the common choice for resizing and generating AVIF/WebP.
- —Bundler plugins such as vite-imagetools: generate sizes and formats at import time via query parameters.
- —Squoosh (web app): handy for comparing individual images side by side and choosing a quality value.
- —SVGO: strips editor metadata and redundant markup from SVG files.
- —CDN-side transforms: some CDNs resize and convert images on request; check the extra cost and caching behaviour.
Keep originals separately in the repository and publish generated files with hashed names so long-lived cache rules are safe. Stripping EXIF data (including location) during generation reduces size and is the right call for privacy.
Local support
Image optimization checklist
An image optimization checklist is a short set of items to verify on every page template before launch. Lighthouse and PageSpeed Insights flag most of them, but real-user data is what counts; watch the Core Web Vitals report in Search Console during the weeks after launch.
- —Photos in AVIF or WebP, logos and icons in SVG.
- —Every image has srcset and a sizes value that matches the real layout.
- —Every img tag has width and height, or its container has aspect-ratio.
- —No lazy loading on the LCP image, and fetchpriority="high" is set on it.
- —loading="lazy" on images below the first screen.
- —Meaningful alt text on content images, empty alt on decorative ones.
- —The build pipeline generates sizes and formats from originals and strips EXIF.
