| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
ย | ย | |||
ย | ย | |||
ย | ย | |||
ย | ย | |||
Repository to curated best practices for web images
This repository focuses on Web Performance best practices for images. Performance optimization does not replace accessibility: attributes like alt are essential and are out of scope for this guide.
We can use media to define a media query as a breakpoint to load a responsive image.
<picture>
<source srcset="image-ultrawide.jpg" media="(min-width: 1200px)">
<source srcset="image-wide.jpg" media="(min-width: 600px)">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>In the above code, the browser loads the image.jpg in mobile version, the image-wide.jpg in tablets resolutions or bigger, and the image-ultrawide.jpg will be loaded in screen resolutions bigger than 1200px. NOTE: Browser finds the first matching condition and ignores everything after.
We can use sizes, that allows you to specify the layout width of the image for a list of media conditions.
<img
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
sizes="(min-width: 600px) 600w,
(min-width: 1200px) 1200w"
src="image.jpg" height="300" width="200" alt="Awesome image">In the above code (like with media), the browser loads the image.jpg in mobile version, the image-wide.jpg in tablets resolutions or bigger, and the image-ultrawide.jpg will be loaded in screen resolutions bigger than 1200px.
Usually, the next-gen image formats like WebP, AVIF or JPEG XL, can optimize image weight while maintaining good image quality.
JPEG XL offers the best compression of all modern formats and is worth watching closely. As of 2025, support is available in Safari and Firefox (experimental). Chrome removed support in 2022 but the discussion remains open. Including it as the first <source> is safe: browsers that don't support it will simply fall through to the next format.
With the HTML tag <picture> we can specify the type in the <source> tag. The type is the image format, and we can use it to serve modern image formats. The browser will use the first image format that it supports.
<picture>
<source type="image/webp" srcset="image.webp">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>In the above code, the browser loads the first image format that it can render. E.g. Internet Explorer 11 nor Safari 13 can't load the WebP image format (a next-gen image format), so they will load the JPEG image.
<picture>
<!-- JPEG XL: best compression, supported in Safari and Firefox (experimental) -->
<source type="image/jxl" srcset="image.jxl">
<source type="image/avif" srcset="image.avif">
<source type="image/webp" srcset="image.webp">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>In the above code, we list modern image formats from "more optimized" to "less optimized". The browser will show the first image that it can load and render.
We can combine both approaches to serve modern image formats and responsive images to load the best image on each device.
<picture>
<!-- JPEG XL: best compression, supported in Safari and Firefox (experimental) -->
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jxl 600w,
image-ultrawide.jxl 1200w"
type="image/jxl">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.avif 600w,
image-ultrawide.avif 1200w"
type="image/avif">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.webp 600w,
image-ultrawide.webp 1200w"
type="image/webp">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jpeg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>In the above code, we have a combination of all modern image formats and the sizes needed. This is an example, as every site or project needs different sizes.
We can use several attributes to improve the Web Performance, aka user experience.
<picture>
<!-- JPEG XL: best compression, supported in Safari and Firefox (experimental) -->
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jxl 600w,
image-ultrawide.jxl 1200w"
type="image/jxl">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.avif 600w,
image-ultrawide.avif 1200w"
type="image/avif">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.webp 600w,
image-ultrawide.webp 1200w"
type="image/webp">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jpeg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img
loading="lazy"
decoding="async"
src="image.jpg"
height="300"
width="200"
alt="Awesome image">
</picture>In the above code, we have a better code for all the images below the fold (outside the viewport) and the browser does not load these images (according to the threshold)
The previous code covers the scenario for all images outside of the viewport. Usually, the LCP metric refers to an image element, so we can iterate to improve it.
<picture>
<!-- JPEG XL: best compression, supported in Safari and Firefox (experimental) -->
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jxl 600w,
image-ultrawide.jxl 1200w"
type="image/jxl">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.avif 600w,
image-ultrawide.avif 1200w"
type="image/avif">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.webp 600w,
image-ultrawide.webp 1200w"
type="image/webp">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jpeg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img
fetchpriority="high"
decoding="sync"
src="image.jpg"
height="300"
width="200"
alt="Awesome image">
</picture>In the above code, we changed the attribute decoding to sync to prioritize the decoding, removed the attribute loading because the default behavior is eager, so we don't need it, and we add the attribute fetchpriority="high" to indicate to the browser to load the image as soon as possible.
By the way, we don't need to add these attributes to all <source> tags, only to <img> tags.
fetchpriority="high" boosts the download priority, but only once the browser has parsed the <img> tag in the HTML. If the LCP image is discovered late โ inside a component, a carousel, or injected via JavaScript โ the browser may start downloading it too late.
A <link rel="preload"> in the <head> solves this: it starts the download before the parser reaches the image.
<!-- In the <head>: start the download as early as possible -->
<link rel="preload" as="image" href="image.jpg" fetchpriority="high">If you use srcset with modern formats, the preload tag supports imagesrcset and imagesizes so the browser preloads exactly the resource it will use:
<link
rel="preload"
as="image"
imagesrcset="image-wide.avif 600w, image-ultrawide.avif 1200w"
imagesizes="(min-width: 600px) 100vw, 600px"
fetchpriority="high">See Optimize when the resource is discovered for more details.
We see that we need a lot of code to deliver the best user experience ๐.
We can use a CDN Image Service like Cloudinary to remove the part of code that references the image format, as these services serve the best image format supported by the browser.
The browser sends in the HTTP Headers the accept header value to indicate which content types, expressed as MIME types.
In the screenshot above we see that my current version browser supports image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;, so with a service of automatic image format, the service will send me an avif image format in response.
<picture>
<img
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="
https://res.cloudinary.com/nucliweb/image/upload/c_scale,f_auto,w_600/v1010101010/demo/image.png 600px,
https://res.cloudinary.com/nucliweb/image/upload/c_scale,f_auto,w_1200/v1010101010/demo/image.png 1200px"
src="https://res.cloudinary.com/nucliweb/image/upload/f_auto/v1010101010/demo/image.png"
loading="lazy"
decoding="async"
height="300"
width="200"
alt="Awesome image">
</picture>Now we don't need to define the type because this is transparent to us. The service sends the best image format supported by the browser. Notice that the image in the samples is an PNG image, yet the browser will load an avif image format.
| Back | FazBrowse Home | New Git URL |