Image Optimization Guide: Formats, Responsive Images, Delivery
Images account for more than half of the total bytes transferred on the average web page. More importantly, an image is the Largest Contentful Paint (LCP) element on roughly 70 to 80 percent of pages, which means image loading performance is directly tied to your most important Core Web Vitals score. No other category of optimization delivers faster, more measurable gains than getting images right.
This guide covers the full stack of image optimization: selecting the correct modern format (AVIF, WebP, JPEG, PNG, SVG), implementing responsive images with srcset, sizes, and the picture element so browsers always download the smallest appropriate file, and delivering images efficiently using CDN transforms, fetchpriority, and lazy loading. Every technique includes working code examples you can apply today regardless of your framework or CMS.
Why image optimization is the highest-leverage performance work
Web performance work competes for engineering time, and it helps to know where each hour will have the most impact. Images win this competition for most sites because the gains are large, predictable, and do not require architectural changes.
Consider what happens when a page loads. The browser requests the HTML document, discovers resource URLs, and queues downloads based on priority. For most pages, the largest queued download is an image -- the hero photograph, the product shot, the featured article image. That download sits directly on the critical path to LCP. A 400 KB unoptimized JPEG that takes 1.8 seconds to download on a median mobile connection becomes a 90 KB AVIF that downloads in 400 milliseconds. The LCP improvement is nearly 1.4 seconds from a single file change, with zero JavaScript involved.
The business case is equally clear. Google uses Core Web Vitals as a search ranking signal, and LCP is the metric most directly affected by image performance. A poor LCP score can suppress organic search rankings even when other technical SEO work is excellent. Beyond ranking, research from Google and Deloitte shows a strong correlation between faster LCP and higher conversion rates: each 100ms improvement in LCP correlates with roughly a 1% lift in conversions.
Image optimization also compounds with other improvements. Faster image downloads free up bandwidth for fonts, scripts, and API calls. Correctly sized images eliminate unnecessary decode work on low-end devices. Lazy loading reduces the initial payload and lets the browser focus resources on above-the-fold content. Together, these effects ripple through every performance metric, not just LCP.
The remaining question is where to start. The answer is almost always: audit your LCP image first. Open Chrome DevTools, record a Performance trace, and find the LCP marker. Note the URL of the LCP element, its file size, its format, and whether it has appropriate width and height attributes. That single audit typically reveals three to five high-impact opportunities. The sections below cover each one in depth.
Choose the right format (AVIF, WebP, JPEG, PNG, SVG)
Format selection is the highest-leverage encoding decision you will make. The same image content encoded in AVIF instead of JPEG is typically 40 to 55 percent smaller at equivalent visual quality. At scale, across millions of page views, that difference defines your infrastructure costs and your users' experience on constrained mobile connections.
AVIF
AVIF (AV1 Image File Format) is derived from the AV1 video codec and offers the best compression of any widely supported format. It excels at photographic content, gradients, and images with fine detail. Browser support reached broad availability with Chrome 85 (August 2020), Firefox 93 (October 2021), and Safari 16 (September 2022). As of 2026, over 92% of global users are on browsers that support AVIF, making it safe to serve as the primary format with fallbacks for the remainder.
The main tradeoff is encoding time. AVIF encoding is computationally expensive compared to JPEG or WebP -- a high-quality AVIF encode can take 10 to 30 times longer than a JPEG. This rules out real-time on-the-fly encoding for most self-hosted setups, but CDN image services handle the encoding once and cache the result, eliminating the performance cost entirely for end users.
WebP
WebP, developed by Google, offers a strong middle ground: 25 to 35 percent smaller than JPEG, universal browser support (including all major browsers since 2020), and fast encoding suitable for real-time transforms. WebP supports both lossy and lossless compression, transparency (replacing PNG in many use cases), and animation (replacing GIF). It should be your fallback format when AVIF is unavailable, and your primary format if your tooling does not yet support AVIF.
JPEG
JPEG remains the universal fallback for photographic content. Use JPEG as the final fallback in <picture> source lists, or as your only format for legacy tooling that cannot produce WebP or AVIF. When encoding JPEG, use progressive JPEG encoding (it renders incrementally as it downloads), set quality between 75 and 85 (higher rarely produces visible improvement), and enable chroma subsampling (4:2:0 for photos, 4:4:4 for images with text or sharp edges).
PNG
Use PNG only when you genuinely need lossless quality or full transparency that WebP cannot satisfy. PNG files are typically two to four times larger than equivalent WebP or AVIF files for photographic content. For UI elements, icons, and illustrations with transparency, prefer WebP or SVG. If you must serve PNG, run it through a lossless optimizer like oxipng or pngcrush to remove metadata and redundant chunks without quality loss.
SVG
SVG is the correct choice for logos, icons, illustrations, charts, and any graphic defined by geometric shapes rather than pixel data. SVGs are resolution-independent (they look sharp at any size on any screen density), can be styled with CSS, and are often just a few kilobytes. Run SVGs through SVGO to remove editor metadata, redundant attributes, and unnecessary precision. For inline SVGs embedded in HTML, the browser avoids an additional HTTP request entirely.
Format decision guide
Use this decision tree for format selection:
- Photograph or complex image: AVIF (primary) + WebP (fallback) + JPEG (final fallback)
- Image with transparency: AVIF (primary) + WebP (fallback) + PNG (final fallback)
- Logo, icon, or geometric illustration: SVG
- Animated content: Video (MP4/WebM) rather than GIF or animated WebP for anything over 5 frames
Responsive images with srcset, sizes, and picture
Serving a 2400px wide image to a mobile device that will display it at 390px is one of the most common and costly mistakes in web performance. The browser downloads every byte of a 600 KB file, decodes it, scales it down, and discards the extra pixels. The srcset and sizes attributes solve this by letting the browser choose the most appropriate source.
The srcset and sizes pattern
The srcset attribute provides a list of image sources with their intrinsic widths. The sizes attribute describes how wide the image will be displayed at each viewport width. The browser uses both to calculate the required pixel density and select the smallest source that meets it.
<!-- Responsive image: browser picks the right size for the device -->
<img
src="/images/hero-800.jpg"
srcset="
/images/hero-400.avif 400w,
/images/hero-800.avif 800w,
/images/hero-1200.avif 1200w,
/images/hero-1600.avif 1600w
"
sizes="(max-width: 768px) 100vw,
(max-width: 1280px) 80vw,
1280px"
width="1600"
height="900"
alt="Dashboard showing Core Web Vitals metrics over time"
fetchpriority="high"
>
The sizes attribute above tells the browser: on viewports up to 768px, the image is full viewport width; on viewports up to 1280px, it is 80% of the viewport; beyond that it is always 1280px wide. The browser multiplies that display width by the device pixel ratio, then picks the smallest srcset candidate that is at least that many pixels wide.
Always include width and height attributes matching the intrinsic dimensions of the image. This lets the browser calculate the aspect ratio and reserve space before the image loads, preventing layout shift (which harms CLS) and avoiding costly reflows during paint.
The picture element for format switching
The <picture> element lets you specify multiple <source> elements with different type attributes. The browser picks the first source whose format it supports and ignores the rest. This is the standard pattern for serving AVIF to supporting browsers with WebP and JPEG as progressive fallbacks.
<picture>
<!-- AVIF: best compression, modern browsers -->
<source
type="image/avif"
srcset="
/images/hero-400.avif 400w,
/images/hero-800.avif 800w,
/images/hero-1200.avif 1200w
"
sizes="(max-width: 768px) 100vw, 80vw"
>
<!-- WebP: good compression, universal support -->
<source
type="image/webp"
srcset="
/images/hero-400.webp 400w,
/images/hero-800.webp 800w,
/images/hero-1200.webp 1200w
"
sizes="(max-width: 768px) 100vw, 80vw"
>
<!-- JPEG: universal fallback -->
<img
src="/images/hero-800.jpg"
srcset="/images/hero-400.jpg 400w, /images/hero-800.jpg 800w, /images/hero-1200.jpg 1200w"
sizes="(max-width: 768px) 100vw, 80vw"
width="1200"
height="675"
alt="Dashboard showing Core Web Vitals metrics over time"
fetchpriority="high"
>
</picture>
Art direction with picture
The picture element is also the correct tool for art direction -- serving different image crops or compositions at different viewport sizes. A landscape hero photograph might look great at desktop widths but have its subject too small on mobile. Use media attributes on <source> to serve a portrait crop for narrow viewports.
<picture>
<!-- Portrait crop for mobile -->
<source
media="(max-width: 767px)"
type="image/avif"
srcset="/images/hero-portrait-400.avif 400w, /images/hero-portrait-800.avif 800w"
sizes="100vw"
>
<!-- Landscape crop for desktop -->
<source
media="(min-width: 768px)"
type="image/avif"
srcset="/images/hero-landscape-800.avif 800w, /images/hero-landscape-1600.avif 1600w"
sizes="80vw"
>
<img
src="/images/hero-landscape-800.jpg"
width="1600"
height="900"
alt="A product team reviewing performance dashboards on a large monitor"
>
</picture>
For React or Next.js projects, the Next.js <Image> component abstracts srcset, sizes, and picture into a single component with automatic format negotiation. See the responsive images fix guide for framework-specific implementation patterns.
Delivery: CDN transforms, fetchpriority, and lazy loading
Choosing the right format and writing correct srcset markup are encoding-time decisions. Delivery decisions happen at request time and determine how quickly those optimized images reach the browser. Three tools have the most impact: CDN image transforms, the fetchpriority attribute, and loading="lazy".
CDN image transforms
Managing static image variants manually (one AVIF, one WebP, one JPEG, each at four widths, for hundreds of images) is untenable at scale. CDN image transform services solve this by generating and caching variants on demand from a single source file. You upload one high-quality original; the CDN generates the correct format, dimensions, and quality level based on URL parameters or request headers.
Leading services include Cloudflare Images, Imgix, Cloudinary, Fastly Image Optimizer, and Vercel Image Optimization. Each lets you reference a source image and append transform parameters to the URL:
<!-- Cloudflare Images transform URL pattern -->
<img
srcset="
/cdn-cgi/image/width=400,format=avif/hero.jpg 400w,
/cdn-cgi/image/width=800,format=avif/hero.jpg 800w,
/cdn-cgi/image/width=1200,format=avif/hero.jpg 1200w
"
sizes="(max-width: 768px) 100vw, 80vw"
src="/cdn-cgi/image/width=800,format=jpeg/hero.jpg"
width="1200"
height="675"
alt="Hero image served via CDN transforms"
>
<!-- Imgix URL pattern -->
<img
srcset="
https://example.imgix.net/hero.jpg?w=400&fm=avif&auto=compress 400w,
https://example.imgix.net/hero.jpg?w=800&fm=avif&auto=compress 800w,
https://example.imgix.net/hero.jpg?w=1200&fm=avif&auto=compress 1200w
"
sizes="(max-width: 768px) 100vw, 80vw"
src="https://example.imgix.net/hero.jpg?w=800&fm=jpeg"
width="1200"
height="675"
alt="Hero image served via Imgix transforms"
>
CDN transforms also handle the AVIF encoding cost transparently. The first request for a new variant triggers the encode; all subsequent requests are served from cache. This makes AVIF practical even for sites that cannot incorporate encoding into their build pipeline.
fetchpriority
The browser assigns a download priority to every resource it discovers. By default, images discovered during HTML parsing receive "Low" or "Medium" priority. The LCP image needs "High" priority so the browser starts downloading it immediately, ahead of non-critical resources.
The fetchpriority attribute (supported in Chrome 101+, Safari 17.2+, Firefox 132+) lets you override this default:
<!-- Boost priority for the LCP image -->
<img
src="/hero.avif"
fetchpriority="high"
width="1200"
height="675"
alt="Hero image"
>
<!-- Drop priority for decorative below-fold images -->
<img
src="/decoration.webp"
fetchpriority="low"
loading="lazy"
width="400"
height="300"
alt="Decorative background pattern"
>
<!-- Preload the LCP image in head for maximum priority -->
<link
rel="preload"
as="image"
href="/hero.avif"
fetchpriority="high"
imagesrcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w"
imagesizes="(max-width: 768px) 100vw, 80vw"
>
Pair fetchpriority="high" on the image element with a <link rel="preload"> in the document <head>. The preload link starts the download during the browser's initial HTML scan, before the parser reaches the <img> element in the body. This technique alone can reduce LCP by 200 to 500 milliseconds on pages with a significant amount of HTML before the image.
For more detail on implementing fetchpriority with the LCP image and other image optimization techniques that directly affect LCP, see the dedicated fix guide.
Lazy loading
Native lazy loading (loading="lazy") defers image downloads until the image approaches the viewport. It is supported in all modern browsers and requires no JavaScript. Applying it to all images below the fold reduces the initial page weight, speeds up LCP (by reducing network contention for the LCP image), and lowers data costs for users who never scroll to the bottom of a page.
The critical rule: never apply loading="lazy" to the LCP image. The LCP image is by definition in the initial viewport, and lazy loading will defer its download, directly increasing LCP. Apply lazy loading starting from the second or third image on the page, depending on layout. For image grids, carousels, and blog post thumbnails, lazy loading is always appropriate.
For more detail on lazy loading implementation patterns and common mistakes, see the fix guide.
Common mistakes
Even teams that know the theory make these errors consistently. Each one has a measurable negative impact on LCP or CLS.
-
Lazy-loading the LCP image. This is the single most common image performance mistake. Many developers apply
loading="lazy"globally to all images via a CMS plugin or image component setting. The LCP image is then deferred, adding hundreds of milliseconds to LCP. Always check that your LCP image hasfetchpriority="high"rather thanloading="lazy". -
Missing width and height attributes. Without explicit dimensions, the browser cannot allocate space for the image during layout. When the image finally loads, it causes a reflow and layout shift that harms CLS. Set
widthandheighton every<img>element matching the intrinsic pixel dimensions of the source file. CSS can still control the rendered size -- these attributes only set the aspect ratio for the browser's space reservation. -
Serving a single large image to all devices. Deploying a 2400px JPEG as the only source means mobile users on 390px screens download 6 times more pixels than they will display. Always implement
srcsetandsizeswith breakpoints that match your actual layout, or use a CDN transform to generate widths on demand. -
Not providing a sizes attribute. Writing
srcsetwithoutsizescauses the browser to assume the image is 100vw (full viewport width). On a desktop with a sidebar layout where the image is actually 50% wide, the browser will download twice the pixels needed. Always write accuratesizesvalues based on your CSS layout at each breakpoint. - Using images for text or SVG content. Screenshots of text, logos embedded as JPEG, and simple geometric icons served as PNG files waste bandwidth and sacrifice quality. Export text-based content as SVG and run it through SVGO. Use CSS typography for text that appears alongside images. SVGs are infinitely scalable and typically a fraction of the file size of a rasterized equivalent.
-
Overusing fetchpriority="high". Setting high priority on multiple images on the same page defeats the purpose. The browser now treats several resources as equally urgent, which creates contention rather than prioritization. Reserve
fetchpriority="high"for one image per page -- the LCP element -- and leave all other images at their default priority or set non-critical images tofetchpriority="low".
Tools and validation
Identifying image optimization opportunities and verifying that fixes have worked requires a combination of field measurement, lab auditing, and automated CI tooling.
Field measurement
Google PageSpeed Insights combines Lighthouse lab analysis with Chrome User Experience Report (CrUX) field data for your URL. The field data shows your real-user LCP at the 75th percentile -- the number Google uses for ranking. PageSpeed Insights also provides a "Largest Contentful Paint element" diagnostic that names the specific image causing LCP.
Google Search Console Core Web Vitals report shows field LCP across your entire site, grouped by URL pattern. Use this to identify which templates or page types have the worst image performance before deciding where to focus optimization work.
web-vitals JavaScript library (npm: web-vitals) lets you measure LCP in your own real-user monitoring pipeline. Collect the onLCP callback and log the element URL and file size alongside session data to segment by device type, geography, and connection speed.
Lab and audit tools
Lighthouse (built into Chrome DevTools, or run via CLI) provides the "Properly size images," "Serve images in next-gen formats," "Efficiently encode images," and "Defer offscreen images" audits. Each audit shows estimated byte savings and lists the specific images that need attention.
Squoosh (squoosh.app) is a browser-based image compression tool from Google Chrome Labs. It lets you compare AVIF, WebP, and JPEG compression side by side with visual quality controls and shows the exact file size of each encoding. Use it to evaluate format and quality decisions before committing to a pipeline change.
Sharp (npm: sharp) is the standard Node.js image processing library. It wraps libvips and supports encoding to AVIF, WebP, JPEG, and PNG with fine-grained quality and compression controls. Use it in build scripts, serverless functions, or CI pipelines to generate responsive image variants automatically.
ImageOptim (macOS) provides a drag-and-drop interface for lossless optimization of JPEG, PNG, and GIF files. It runs a suite of optimizers (MozJPEG, PNGCRUSH, Zopfli) automatically. Use it for images that must remain in legacy formats.
CI integration
Lighthouse CI (github.com/GoogleChrome/lighthouse-ci) runs Lighthouse on every pull request and fails the build if performance budgets are exceeded. Configure the "offscreen-images" and "uses-optimized-images" audits in your lighthouserc.js budget file to catch regressions before they reach production.
Calibre and SpeedCurve provide continuous synthetic monitoring with LCP time-series data. Both support alerting when LCP degrades beyond a configured threshold, making them useful for catching the impact of new image uploads or CMS changes.
See the WebVitals.tools glossary for definitions of LCP, CLS, TTFB, and other metrics referenced in tool reports.
Frequently asked questions
Use both via the picture element: offer AVIF first, then WebP as a fallback, then JPEG as the final fallback. AVIF typically saves 50% over JPEG and 20% over WebP, but requires slightly more CPU to encode and decode. For most production sites, the size savings of AVIF outweigh the encoding cost, especially when offloaded to a CDN image transform service. See the AVIF section above for browser support details.
srcset on an img element handles resolution and size variants of the same image -- the browser picks the most appropriate source based on device pixel ratio and viewport size. The picture element adds art direction: you can serve completely different images (different crops, compositions, or even formats) depending on media conditions. Use srcset for responsive sizing of the same image; use picture when you need to swap format or change the image composition entirely.
No. Never apply loading="lazy" to your LCP image (usually the hero image above the fold). Lazy loading defers the download until the element enters the viewport, which delays LCP significantly. Only lazy-load images that start below the fold. For the LCP image, use fetchpriority="high" and add a preload link in <head> instead. For the full explanation and a before/after waterfall comparison, see the lazy loading fix guide.
Real-world savings vary by image content, but typical benchmarks show AVIF saving 40-55% over JPEG and WebP saving 25-35% over JPEG at equivalent visual quality. For PNG assets with transparency, WebP typically reduces file size by 25-40%, while AVIF often achieves 50-65% savings. The exact savings depend on image complexity, detail level, and the quality setting used during encoding. Use Squoosh to evaluate your specific images before committing to a format decision.
For a full-width hero with a max container width of 1280px, a reasonable sizes attribute is: sizes="(max-width: 1280px) 100vw, 1280px". This tells the browser the image is 100% of the viewport width on smaller screens and capped at 1280px on larger screens. Always measure your actual layout and write sizes based on real CSS breakpoints rather than guessing -- inaccurate sizes values can cause the browser to download the wrong variant.
Related reading
Continue your image optimization work with these targeted guides and fix pages:
Complete LCP Guide
Everything about Largest Contentful Paint: causes, measurement, and the full optimization playbook.
FixFix LCP: Image Optimization
Step-by-step instructions to reduce LCP by optimizing your page's largest image element.
FixFix LCP: Responsive Images
Implement srcset, sizes, and picture correctly in React, Next.js, Vue, and plain HTML.
FixFix LCP: Lazy Loading
When and how to apply loading=lazy without hurting LCP -- including the common mistake of lazy-loading the hero image.
ReferenceWebVitals Glossary
Definitions for LCP, CLS, INP, TTFB, CrUX, fetchpriority, and every other term used in Core Web Vitals documentation.