Contenu principal

Optimiser les images sur le web : checklist des bonnes pratiques

lundi 24 juillet 2023

Dans les articles prĂ©cĂ©dents, je vous parlais de pourquoi il est important d’optimiser la performance de ses pages web. Notamment, mesurer le Largest Contentful Paint (LCP) est un bon moyen de mesurer Ă  partir de quand vos utilisateurices ont l’impression que votre page est chargĂ©e.

Pour améliorer ce LCP, nous avons vu que bien maßtriser son réseau était important. De plus, en commençant à peine à effleurer le sujet des images avec des histoires de Lazyload et de Preload, nous avons gagné 20% (!) sur le LCP. Les images sont donc essentielles quand on parle de performance.

Dans cet article, nous allons continuer à améliorer celles-ci en approfondissant la gestion des images et notamment le comportement des balises <picture>, <source> et <img>. A la fin, vous en sortirez avec une méthode pour choisir les bons formats et les bonnes tailles en toute circonstance.

Si vous ressentez le besoin d’avoir plus d’accompagnement dans votre entreprise pour mettre en place ces sujets, n’hĂ©sitez pas Ă  me contacter par mail. Peut-ĂȘtre pouvons nous travailler ensemble afin de poser les premiĂšres briques ?

Rappels sur l’optimisation rĂ©seau

Afin de commencer à rentrer dans le dur du sujet, rappelons déjà les premiÚres optimisations que vous pouvez faire à moindre coût :

  1. les images sont correctement compressées

GĂ©nĂ©ralement, vos images seront du jpg et/ou du png. Une image bien optimisĂ©e peut faire toute la diffĂ©rence en termes de poids de fichier (jusqu’à -80%).

Passez donc vos images dans Squoosh avant de les afficher sur vos pages.

Toutefois, la tentation est grande de sacrifier la qualitĂ© des images pour diminuer encore plus son poids. Est-ce que ces kilo octets valent toujours le coup ? Pour certain·e·s, oui. Pour d’autres, non. C’est pour cette raison que Squoosh est un bon outil : il vous permettra de choisir le niveau de qualitĂ© pertinent pour vous.

  1. les images qui ne sont pas visibles sont lazyloadées

En ayant beaucoup d’images dans votre site, vous pouvez saturer la bande passante de vos utilisateurices. Pour Ă©viter cela, toutes les images qui ne sont pas directement visibles au chargement de la page (= sans avoir besoin de scroll), doivent ĂȘtre marquĂ©es comme lazy.

 <img
     alt="Le plus choupichou des choupichats"
     src="cat.jpg"
     width="150"
     height="150"
+    loading="lazy"
 >

Selon votre parc utilisateur, ce n’est peut ĂȘtre pas encore parfaitement supportĂ©. Mais nous en sommes quand mĂȘme Ă  93% du web Ă  date d’écriture de cet article.

  1. Preloader et fetchpriority pour votre image principale

Dans votre trace rĂ©seau, vous aurez peut-ĂȘtre des ressources qui passeront devant votre image principale. Afin d’éviter cela vous pouvez utiliser une balise <link> dans votre <head> afin d’aider le navigateur Ă  mieux comprendre vos prioritĂ©s.

<link rel="preload" as="image" type="image/jpeg" href="/images/banner.jpg" fetchpriority="high" />

Utiliser <picture> pour télécharger la bonne image en fonction du navigateur

Les optimisations ci-dessus reposent sur le principe qu’une seule image est disponible. Cependant, ce qui fait la beautĂ© du web, c’est qu’il est accessible partout, sous plein de formes diffĂ©rentes : de la montre connectĂ©e, en passant par le tĂ©lĂ©phone, jusqu’à l’écran TV. Chaque Ă©cran aura son format, sa rĂ©solution, ses contraintes.

Prenons l’exemple de cette image en bas de page sur notre site d’exemple :

Screenshot qui vient de la page d'exemple qui montre le changement de layout d'un block si on est sur mobile (320px) ou tablette (800px). Le ratio de l'image selon l'appareil change complĂštement.
Démonstration d'une image qui passe d'un format paysage à portrait selon la taille du navigateur

La partie visible de l’image sur mobile est beaucoup plus resserrĂ©e en hauteur que sur desktop. Il est donc dommage d’envoyer l’image desktop sur mobile ou inversement. C’est d’autant plus vrai que je n’ai pris que 2 screenshots. Mais si on rĂ©cupĂšre toutes les tailles, on obtient ce graphique :

Graphique avec 2 courbes : la largeur et la hauteur de l'image en fonction de la largeur de l'écran
Largeur et hauteur de l'image en fonction de la taille de l'écran

💡 Manuellement, il est laborieux de rĂ©cupĂ©rer ces courbes. Je vous conseille de passer par ce Snippet que vous pouvez exĂ©cuter directement dans la console de votre navigateur.

https://gist.github.com/JulienPradet/abfbff6577ecebd3d1ffe72f6063b1f7

En suivant les instructions de celui-ci, vous pourrez rĂ©cupĂ©rer dans votre presse-papiers la largeur et la hauteur de votre image en fonction de la taille de l’écran. Il ne vous restera plus qu’à coller ça dans une feuille de calcul (Google Sheets/Excel/Libre Calc) pour en gĂ©nĂ©rer un graphique.

Dans notre exemple, on se rend compte qu’au minimum l’image fait 256x208 et au maximum 532x208. Pour donner une Ă©chelle de comparaison, c’est du simple au double. Si l’image avait grandit verticalement, l’ordre de grandeur aurait Ă©tĂ© multiplĂ© d’autant.

Avec toutes ces informations, comment on s’en sort pour livrer la bonne image au bon navigateur ?

Méthode pour choisir les bonnes images

1. Définir les breakpoints

La premiĂšre Ă©tape est de connaĂźtre votre breakpoints : quelles sont les tailles d’écran pour lesquelles vous allez choisir de charger une image diffĂ©rente ? Cela va dĂ©pendre de vos media queries en CSS et de vos utilisateurices.

Dans le CSS de notre page d’exemple, nous avons :

  • 600
  • 1000

On les voit bien au niveau du graphe parce que cela correspond aux cassures sur les courbes. Cela dit, dans nos calculs d’images, nous n’allons pas les prendre tels quels, mais leur soustraire un pixel (599 et 999). En effet, c’est Ă  ces valeurs-lĂ  que les images sont gĂ©nĂ©ralement plus grandes.

Ensuite, vos utilisateurices auront certainement des rĂ©solutions diffĂ©rentes. Notamment 375px de largeur est un format encore trĂšs rĂ©pandu aujourd’hui pour les iPhones. Les tablettes sont environ vers 768px. Et sur ordinateur, on a l’habitude du 1920x1080. C’est vrai chez les devs, mais chez beaucoup de gens, les Ă©crans de laptops sont par dĂ©faut zoomĂ©s Ă  150%, donc c’est plutĂŽt du 1280x720.

💡 Il est difficile d’avoir des ressources fiables Ă  ce sujet. Il est donc vivement conseillĂ© de sortir ces donnĂ©es de vos analytics. Mais Ă  dĂ©faut, n’hĂ©sitez pas Ă  suivre Andy Bell (@andy@bell.bz) qui a fait une Ă©tude rĂ©cente Ă  ce sujet et publiera plus d’informations bientĂŽt.

Mise Ă  jour : l’étude a Ă©tĂ© rĂ©sumĂ©e ici : https://viewports.fyi/

Pour notre exemple, nous allons donc considérer les tailles suivantes et les récupérer de notre graphe :

  • 375
  • 599
  • 768
  • 999
  • 1280
  • 1920
Le mĂȘme graphique que prĂ©cĂ©demment, mais avec des lignes verticales en pointillĂ© reprĂ©sentant les breakpoints citĂ©s prĂ©cĂ©demment
Tailles de l'image en fonction des différents breakpoints

Enfin, avant de passer Ă  la prochaine Ă©tape, on constate qu’on peut simplifier le nombre de breakpoints parce que certains partagent des tailles d’images trĂšs similaires. C’est le cas par exemple de 1280 & 1920 : les images font toujours 336 x 260.

Par ailleurs, si on regarde entre 375 et 399, l’image grandit assez fortement et a un ratio trĂšs diffĂ©rent. Il est sĂ»rement pertinent de couper la poire en deux et dire que la petite image utilisĂ©e sur une largeur de 375 le sera aussi pour des Ă©crans lĂ©gĂšrement plus grands.

Si on récapitule, nous devons donc générer le tableau suivant :

Device Width Media Query
375 (max-width: 415px)
599 (min-width: 416px) and (max-width: 599px)
999 (min-width: 600px) and (max-width: 999px)
1280 (min-width: 1000px)

Petit point au sujet des media queries

Dans le tableau ci-dessus, je vous ai Ă©crit les media queries de maniĂšre exhaustive. Dans les faits, le navigateur utilisera toujours la premiĂšre source compatible. Donc on peut simplifier lĂ©gĂšrement l’écriture en ne passant que par des min-width (Mobile First) et en inversant l’ordre du tableau.

Device Width Media Query
1280 (min-width: 1000px)
999 (min-width: 600px)
599 (min-width: 416px)
375

La derniĂšre ligne n’a pas de media query parce que ce sera celle utilisĂ©e par dĂ©faut.

💡 Que faire pour les images plein Ă©cran ?

L’exemple que nous avons pris est pour une image qui a une largeur relativement stable et fixe sur tous les Ă©crans. Si je veux rajouter une banniĂšre tout en haut de mon site, comment faire ?

Il faut suivre exactement la mĂȘme mĂ©thode. La seule rĂ©elle diffĂ©rence est que vous ne pouvez pas beaucoup simplifier vos breakpoints (l’image de 1280 ne sera pas du tout la mĂȘme que l’image de 1920). Il y aura donc plus de lignes dans votre tableau.

2. DĂ©finir les tailles d’images pour chaque breakpoints

Maintenant que nous savons quelles tailles d’écran regarder, nous pouvons aller rĂ©cupĂ©rer les tailles d’images :

Device Width Media Query Image Size
1280 (min-width: 1000px) 336 x 240
999 (min-width: 600px) 240 x 300
599 (min-width: 416px) 535 x 208
375 311 x 208

Cela dit, ce n’est pas tout Ă  fait fini. En effet, jusqu’à maintenant on a parlĂ© de pixels. Mais ce ne sont pas de vrais pixels, ce sont des pixels CSS : 1 pixel CSS peut valoir 1, 2 ou 3 pixels physiques selon votre appareil. Par exemple, la plupart des tĂ©lĂ©phones en ont minimum 2. Les derniers iPhones en ont 3. Vous pouvez utiliser window.devicePixelRatio pour connaĂźtre cette valeur.

C’est important parce que cela veut dire que selon l’écran physique utilisĂ© par l’utilisateurice, une image de la bonne taille (ex : 311 x 208 sur un Ă©cran de 375px) pourra paraĂźtre flou. Pour qu’elle soit de bonne qualitĂ© sur un tĂ©lĂ©phone qui a un window.devicePixelRatio de 2, il faudra plutĂŽt fournir une image de 622 x 416.

Nous allons appliquer les rÚgles suivantes pour avoir la totalité de nos images :

  • si width < 1000, mettre Ă  disposition des images 2x
    • inutile de mettre Ă  disposition des images 1x parce que tous les tĂ©lĂ©phones et tablettes aujourd’hui ont au moins un ratio de 2
    • inutile de mettre un ratio de 3x parce qu’il est illusoire de penser que l’Ɠil humain puisse distinguer ce niveau de dĂ©tail (-33% de temps de chargement chez Twitter).
  • si width > 1000, mettre Ă  disposition des images 1x ET 2x
    • beaucoup d’écrans de PC sont encore Ă  une densitĂ© standard
    • mais les Ă©crans Ă  haute densitĂ© existent aussi (par exemple les Ă©crans 4k)

💡 A nouveau, n’hĂ©sitez pas Ă  adapter ces rĂšgles Ă  la population qui visite votre site.

Device Width Media Query CSS Image Size Pixel Ratio Physical Image Size
> 1000 (min-width: 1000px) 336 x 240 1x 336 x 240
2x 672 x 480
<= 999 (min-width: 600px) 240 x 300 2x 480 x 600
<= 599 (min-width: 416px) 535 x 208 2x 1070 x 416
<= 375 311 x 208 2x 622 x 416

Ca fait beaucoup de travail tout ça. Est-ce qu’il n’y a pas moyen de tricher et d’automatiser tout ça ? Ca dĂ©pend.

  • Si votre image est critique / visible dĂšs le chargement de la page, alors non, il n’y a pas de solution miracle si vous souhaitez conserver de bonnes performances. Certains outils peuvent vous mĂącher le travail (ex: Daltons), mais globalement il vous faudra quand mĂȘme gĂ©nĂ©rer le HTML manuellement et comprendre quelles tailles mettre Ă  disposition dans vos sources.
  • Si votre image est non critique, vous pouvez vous reposer sur JavaScript. En effet, en JS vous pouvez rĂ©cupĂ©rer la taille nĂ©cessaire de votre image et du coup changer l’URL de votre image Ă  la volĂ©e pour utiliser la bonne taille. C’est ce que propose par exemple TwicPics (rĂ©cemment rachetĂ© par Frontify). Le seul inconvĂ©nient est que cela va potentiellement gĂ©nĂ©rer une myriade d’images et donc vous bĂ©nĂ©ficierez moins des gains liĂ©s au cache CDNs. Mais pour des images lazyloadĂ©es c’est tout Ă  fait envisageable.

💡 Pourquoi ne peut-on pas utiliser de JavaScript pour les images critiques ?

Parce que, qui dit JavaScript, dit exĂ©cution beaucoup plus tard dans le processus de chargement de votre page. Donc vous perdrez de nombreuses centaines de millisecondes sur votre LCP. Sur les quelques tests que j’ai fait sur un site e-commerce en React, cela reprĂ©sentait une perte de sĂšche de 40% du LCP.

En tout cas, dans ce guide, on a fait le plus dur : on a rĂ©ussi Ă  trouver les diffĂ©rentes images Ă  utiliser en fonction du device. Passons au code !

3. Générer les nouvelles images

Pour gĂ©nĂ©rer ces diffĂ©rentes tailles d’images, vous avez 2 options :

  • soit vous mettez en place une CLI ou votre propre API qui vous permet de gĂ©nĂ©rer les diffĂ©rentes tailles d’images (sharp Ă©tant de trĂšs loin la meilleure librairie pour faire ça dans l’écosystĂšme Node.js aujourd’hui).
  • soit vous passez par un service de redimensionnement d’image comme Cloudinary, imgix, etc. Ces services vous permettent d’uploader une premiĂšre image en grande taille, puis vous met Ă  dispositions des URLs qui, Ă  l’aide de paramĂštres, vous permettent de gĂ©nĂ©rer des tailles Ă  la demande.

A noter qu’ici je parle uniquement de redimensionnement, mais il existe dans sharp ou dans les solutions SaaS beaucoup d’options (cover/crop, focal point, manipulation d’image, etc.). N’hĂ©sitez pas Ă  fouiller dans celles-ci afin de trouver le process qui vous convient le mieux.

4. Transformer notre <img> en <picture>

Une fois les images générées passons au HTML. Initialement, il ressemblait à ceci :

<img
	alt="Description de l'image"
	src="/images/image.jpg"
	loading="lazy"
/>

Pour l’adapter au tableau de la section 2., nous allons devoir utiliser 2 nouvelles balises : <source> et <picture>.

  • <source> dĂ©finit le type de sources disponibles grĂące Ă  ses attributs
    • media : indique le format d’écran pour lequel la source est Ă©ligible
    • srcset : indique quelle image charger en fonction de la densitĂ© de l’écran
  • <picture> indique que plusieurs sources d’images sont disponibles et qu’il peut choisir la premiĂšre qui est compatible. En plus de la liste de <source>, il faut conserver la balise <img> en tant qu’enfant de <picture>, elle servira de source par dĂ©faut pour votre navigateur.

Si on fait le rapprochement avec notre tableau, cela veut dire qu’on va avoir :

  • une <source> par ligne du tableau, en reprenant la media query
  • une URL d’image dans srcset par pixel ratio
  • la balise <img> qui correspond Ă  la derniĂšre ligne du tableau
<picture>
	<source
		media="(min-width: 1000px)"
		srcset="
			/images/image-336x240.jpg 1x
			/images/image-672x480.jpg 2x
		"
	/>
	<source
		media="(min-width: 600px)"
		srcset="/images/image-480x600.jpg 2x"
	/>
	<source
		media="(min-width: 416px)"
		srcset="/images/image-1070x416.jpg 2x"
	/>
	<img
		alt="Description de l'image"
		src="/images/image-622x416.jpg"
		loading="lazy"
	/>
</picture>

Et voilĂ , on a rĂ©ussi Ă  fournir la bonne taille au navigateur ! Si vous inspectez votre navigateur, vous verrez qu’à chaque rafraĂźchissement de page dans des dimensions diffĂ©rentes, c’est la bonne image qui est chargĂ©e.

5. FAQ ❓

Si vous vous ĂȘtes dĂ©jĂ  frottĂ© aux images responsives, aux srcset et compagnie, vous trouvez peut-ĂȘtre que je suis allĂ© un peu vite en besogne. N’hĂ©sitez pas Ă  dĂ©plier les questions ci-dessous pour entrer plus en dĂ©tails dans certains aspects.

Comment se comporte la balise <picture> d'un point de vue CSS ?

GrossiĂšrement, vous pouvez considĂ©rer qu’une <picture> se comporte comme un <span>. Ca n’affiche rien de particulier dans votre page et se comporte donc plus ou moins de la mĂȘme façon que si vous avez une balise <img> directement.

Cela dit, ce nouveau niveau de balise peut parfois poser des problĂšmes quand vous utilisez des Flexbox, des Grids ou autres outils de positionnement. Une solution pour cela est de lui assigner display: contents ou de lui appliquer les mĂȘmes rĂšgles que si vous aviez une div (height: 100%, align-self: stretch ou autre).

Que se passe-t-il si la taille de mon image dépend aussi de la hauteur du navigateur ?

Dans notre exemple nous avions dans notre code CSS uniquement des media queries en largeur (min-width) et pas d’unitĂ©s basĂ©s sur vh. Cependant il est possible que vous ayez besoin de charger une image diffĂ©rente si vous tenez Ă  ce que votre image tienne toujours en hauteur dans votre Ă©cran (ex : max-height: 80vh).

A ce moment là il faudra complexifier votre tableau. Au lieu d’avoir des media queries à une dimension, vous pourrez les combiner : (min-width: 1000px) and (min-height: 720px).

Est-ce possible d'éviter les <source> et de se contenter de srcset et sizes ? On est passé par des descripteurs 1x et 2x mais ce n'est pas mieux de passer par 622w ?

RĂ©ponse courte : non 😁 Si vous passez par ces solutions alternatives, vous vous retrouverez soit avec des images pixellisĂ©es, soit avec des images trop lourdes. Je m’explique.

sizes est un attribut qu’on peut mettre sur <img> (ou sur <source>), pour dĂ©finir quelle taille utiliser en fonction de quelle media query. Si on reprend notre tableau, c’est un peu la combinaison des colonnes Media Query et CSS Image Size. La diffĂ©rence est qu’il n’y a pas besoin de connaĂźtre le type de devices qui visitent notre site parce qu’on se contente de dĂ©crire factuellement les diffĂ©rentes tailles d’images pour tous les devices. Cela va ressembler globalement Ă  quelque chose de ce style :

<img
	sizes="
        (min-width: 1000px) 336px,
        (min-width: 600px) 240px,
        95vw
    "
/>

En combinaison avec l’attribut srcset, cela permet au navigateur, si on lui donne une liste d’image Ă©lligible, de choisir celle qui correspondra le mieux.

<img
	srcset="
		/images/image-336x240.jpg   336w,
		/images/image-480x600.jpg   480w,
		/images/image-622x416.jpg   622w,
		/images/image-672x480.jpg   672w,
		/images/image-1070x416.jpg 1070w
	"
	sizes="
        (min-width: 1000px) 336px,
        (min-width: 600px) 240px,
        95vw
    "
/>

Vous pouvez constater que dans le srcset, j’ai utilisĂ© le descripteur w en fin de ligne. Celui-ci dit au navigateur non pas quelle densitĂ© de pixel a le droit d’utiliser cette image, mais combien de pixels physiques sont prĂ©sents dans l’image.

Charge au navigateur donc de choisir la bonne. Si je suis sur un iPhone de 375px, avec un devicePixelRatio = 3, alors il va comprendre qu’il a besoin d’une image de 375 * 0.95 (grñce à l’attribute sizes) * 3 (devicePixelRatio) = 1068w.

La plus proche est /images/image-1070x416.jpg donc c’est celle là qu’il choisit.

Dommage, nous on lui avait juste besoin de 622x416. Le navigateur aurait pu tĂ©lĂ©charger 40% moins d’octets pour fournir la mĂȘme qualitĂ© de service Ă  l’utilisateurice.

Plus gĂȘnant, il peut arriver des cas oĂč l’image choisie n’aura pas assez de pixels en hauteur. Prenons un exemple trĂšs frĂ©quent : une image de banniĂšre (sizes=“100vw”) qui a la rĂšgle CSS height: 15rem (= 240px). Pour bien faire je vais donc mettre Ă  disposition mes images en x2 sur mobile et x1 + x2 sur desktop.

Device Width Media Query CSS Image Size Pixel Ratio Physical Image Size
1980 (min-width: 1440px) 1980 x 240 1x 1980 x 240
2x 3960 x 480
1439 (min-width: 1280px) 1439 x 240 1x 1439 x 240
2x 2878 x 480
1279 (min-width: 768px) 1279 x 240 1x 1279 x 240
2x 2558 x 480
767 (min-width: 416px) 767 x 240 2x 1534 x 480
375 375 x 240 2x 750 x 480

Et donc avec un srcset unique, cela donneraĂźt ceci (j’ai triĂ© les images par taille croissante pour plus de clartĂ©):

<img
	srcset="
		/images/image-750x480.jpeg   750w,
		/images/image-1279x240.jpeg 1279w,
		/images/image-1439x240.jpeg 1439w,
		/images/image-1534x480.jpeg 1534w,
		/images/image-1980x240.jpeg 1980w,
		/images/image-2558x480.jpeg 2558w,
		/images/image-2878x480.jpeg 2878w,
		/images/image-3960x480.jpeg 3960w
	"
	sizes="100vw"
/>

Donc avec cette nouvelle image, reprenons mon exemple de tout Ă  l’heure : je suis sur un device de 375px avec une densitĂ© 3x. Alors il me faut une image de 1125 pixels physiques. L’image la plus proche est 1279 x 240.

L’utilisateurice se retrouve avec une image qui est maintenant trop grande en largeur, mais trop petite en hauteur. Le ratio est totalement dĂ©formĂ©, et l’image devient floue Ă  l’écran.

Comparaison face à face des deux méthodes de selection des images : sur le premier exemple on voit toute la photo, sur le deuxiÚme l'image est floue, trop zoomée et mal centrée
Comparaison de l'affichage de l'image en 750x480 face Ă  l'image 1279x240

Ce cas est reprĂ©sentatif de la majoritĂ© des images avec lesquelles j’ai pu travailler.

Il reste tout de mĂȘme un dernier cas : celui pour lequel le ratio de l’image est toujours strictement identique. Ainsi, l’image est toujours correctement affichĂ©e, mais il nous reste le problĂšme de la densitĂ© de pixel trop Ă©levĂ©e.

En effet, les derniers iPhones ont une densitĂ© de 3x. Il paraĂźt que c’est censĂ© rendre les affichages plus extraordinaires. En pratique, ça ne sert Ă  rien si ce n’est consommer plus de bande passante et d’énergie. Un jour, peut-ĂȘtre que les navigateurs le gĂ©reront directement. En attendant, limitons manuellement la taille des images tĂ©lĂ©chargĂ©es par exemple en utilisant la mĂ©thode des <source> montrĂ©e plus haut.

Choisir le bon format d’image

Nous avons vu comment choisir la bonne taille pour nos images. Mais il y a un autre levier que nous pouvons utiliser pour améliorer leur performance : le type de fichier (ou format).

Historiquement, les images sur le web étaient réparties sur deux formats :

  • JPEG : trĂšs rĂ©pandu, il permet de choisir son niveau de qualitĂ©. GĂ©nĂ©ralement, on le repĂšre parce qu’il a tendance Ă  crĂ©er des petits artefacts qui dĂ©forment l’image. On prĂ©fĂšre l’utiliser pour toute image qui ne repose pas principalement sur des aplats de couleur.
  • PNG : la seule option pendant longtemps pour gĂ©rer les images transparentes. C’est un format lossless : en l’utilisant, la qualitĂ© de l’image reste intacte. Au-delĂ  des images transparentes, on prefĂšre aussi l’utiliser dĂšs que les images sont constituĂ©es d’applats de couleur, avec peu de dĂ©gradĂ©s. Dans ces conditions, il performe mieux que le JPEG.

Cependant, en allant chercher toujours plus d’optimisations des gens vachement intelligent·e·s ont fait apparaĂźtre d’autres formats :

  • WEBP : plus petit de ~30% que JPEG et PNG, il est capable de gĂ©rer la transparence. De nos jours, ça peut ĂȘtre le nouveau format par dĂ©faut, Ă  quelques exceptions prĂšs (~95% des devices).
  • AVIF : plus petit de ~30% que WEBP, il gĂšre lui aussi la transparence. Il n’est toutefois pas supportĂ© partout avec un support inexistant sur Edge et trĂšs rĂ©cent seulement sur Safari (~82% des devices).
  • JPEG XL : niveau de performance similaire Ă  AVIF. Il semble ĂȘtre meilleur sur des compressions lossless (source : JPEG XL vs AVIF: A Comparison). Cependant, l’intĂ©rĂȘt majeur est vis a vis de son dĂ©codage progressif qui change drastiquement la performance ressentie. Ce format est aujourd’hui uniquement supportĂ© par la toute derniĂšre version de Safari et bientĂŽt sur Firefox. Il est donc trop tĂŽt mais ça s’annonce trĂšs prometteur.

Enfin, je n’en parle pas ici, mais si vous avez des images vectorielles (gĂ©nĂ©ralement pour des logo, des icons ou des schema) vous pouvez directement utiliser du SVG.

Cela fait donc tout un tas de format Ă  avoir en tĂȘte. Mais le niveau de support des navigateurs est trĂšs fragmentĂ©. Comment faire pour gĂ©rer tous les cas ?

Se reposer sur la détection automatique

Pour gĂ©nĂ©rer des images de diffĂ©rentes tailles, je vous parlais tout Ă  l’heure de services en ligne qui vous permettent de redimensionner votre image directement via des paramĂštres au niveau de l’URL. Ces mĂȘmes services proposent gĂ©nĂ©ralement une option pour transformer votre image dans le format le plus optimisĂ©. En effet, lorsque votre navigateur essaye de tĂ©lĂ©charger une image, il va faire une requĂȘte avec un Header Accept qui dĂ©finit quels formats sont supportĂ©s :

Accept: image/avif,image/webp,image/apng,image/svg+xml,image/_,_/\*;q=0.8

Ainsi, Ă  partir de ce header, le serveur qui reçoit la requĂȘte sait qu’il peut renvoyer du AVIF (image/avif) et du WEBP (image/webp) mais pas de JPEG XL par exemple. Pour activer cette option, vous pouvez utiliser les paramĂštres suivants :

💡 Vous avez peut-ĂȘtre entendu parler du fait que le format AVIF est plus lent Ă  gĂ©nĂ©rer que les autres formats. C’était vrai au dĂ©but, mais il paraĂźt que ce n’est plus le cas. Pour cette raison, nombres des services sus-citĂ©s ne proposent pas par dĂ©faut de l’AVIF. N’hĂ©sitez donc pas Ă  vous rĂ©fĂ©rer Ă  leur documentation pour voir comment ils gĂšrent la situation.

Toutefois, vous n’avez pas forcĂ©ment accĂšs Ă  ces outils. Comment faire alors pour le gĂ©rer directement avec du HTML ?

Utiliser l’attribut type sur vos <source>

Tout Ă  l’heure quand nous avions construit le HTML pour mettre Ă  disposition les diffĂ©rentes tailles d’images, je vous avais dit que <source> avait les attributs media et srcset. Il va en accepter un troisiĂšme : type="image/xxx".

En ajoutant ce nouvel attribut, vous indiquez au navigateur qu’il ne doit prendre en compte cette source que s’il supporte le format indiquĂ©. Ainsi, si vous voulez supporter Ă  la fois du jpeg ET du webp, vous allez devoir dupliquer vos sources.

Attention, l’ordre reste important : c’est la premiùre source qui est compatible avec le navigateur qui sera choisie. Les sources de type image/webp doivent donc apparaütre avant les sources de type image/jpeg.

 <picture>
+	<source
+       type="image/webp"
+		media="(min-width: 1000px)"
+		srcset="
+			/images/image-336x240.webp 1x
+			/images/image-672x480.webp 2x
+		"
+	/>
	<source
+       type="image/jpeg"
		media="(min-width: 1000px)"
		srcset="
			/images/image-336x240.jpg 1x
			/images/image-672x480.jpg 2x
		"
	/>
+	<source
+       type="image/webp"
+		media="(min-width: 600px)"
+		srcset="/images/image-480x600.webp 2x"
+	/>
	<source
+       type="image/jpeg"
		media="(min-width: 600px)"
		srcset="/images/image-480x600.jpg 2x"
	/>
+	<source
+       type="image/webp"
+		media="(min-width: 416px)"
+		srcset="/images/image-1070x416.webp 2x"
+	/>
	<source
+       type="image/jpeg"
		media="(min-width: 416px)"
		srcset="/images/image-1070x416.jpg 2x"
	/>
+	<source
+       type="image/webp"
+		srcset="/images/image-622x416.webp"
+	/>
	<img
		alt="Description de l'image"
		src="/images/image-622x416.jpg"
		loading="lazy"
	/>
 </picture>

Comme vous pouvez le voir, cela devient vite trĂšs verbeux. Essayez donc au maximum de vous reposer sur la dĂ©tection automatique. C’est d’autant plus vrai que pour supporter un maximum de navigateurs, il faudrait 4 formats : JPEG, WEBP, AVIF & JPEG XL. Vous vous retrouveriez donc avec 16 balises <source> diffĂ©rentes.

Preload une image responsive

Nous sommes maintenant capables de fournir au navigateur une image de la bonne taille et du bon format en toute circonstance. Il nous reste un dernier point à voir ensemble avant de nous quitter : comment est-ce que ces nouveaux outils impactent le preload des images ?

En effet, nous avions vu dans l’article prĂ©cĂ©dent que preloader les images critiques pouvait avoir un impact sur le LCP. Pour cela, nous avions dĂ» ajouter une balise <link> dans le <head> :

<link
	rel="preload"
	as="image"
	type="image/jpeg"
	href="/images/banner.jpg"
	fetchpriority="high"
/>

Mais comme vous pouvez le voir, cela ne prend en compte qu’une seule URL d’image. Si vous voulez preload la bonne image en fonction de l’appareil utilisĂ©, vous allez devoir multiplier vos balises <link> pour qu’elles ressemblent aux balises <source> de votre image. Cela peut se faire notamment en transformat les attributs:

  • srcset devient imagesrcset
  • sizes devient imagesizes
  • type reste type (facultatif si vous vous reposez sur de la dĂ©tection automatique)
<link
    rel="preload"
    as="image"
    media="(min-width: 1000px)"
    imagesrcset="
        /images/image-336x240.jpg 1x
        /images/image-672x480.jpg 2x
    "
/>
<link
    rel="preload"
    as="image"
    media="(min-width: 600px)"
    imagesrcset="/images/image-480x600.jpg 2x"
/>
<link
    rel="preload"
    as="image"
    media="(min-width: 416px)"
    imagesrcset="/images/image-1070x416.jpg 2x"
/>
<link
    rel="preload"
    as="image"
    href="/images/image-622x416.jpg"
/>

Cependant, simplement renommer les attributs n’est pas suffisant. En effet, si vous mettez ça dans votre page et que vous chargez la page sur desktop, vous constaterez que 4 images seront preloadĂ©es au lieu d’une seule. Cela vient du fait que les balises <link> ne sont pas rassemblĂ©es sous une balise <picture>. Le navigateur ne sait donc pas qu’il en a besoin d’une seule et va toutes les tĂ©lĂ©charger.

Il vous faut donc transformer les media afin qu’elles contiennent un min-width ET un max-width.

 <link
    rel="preload"
    as="image"
    media="(min-width: 1000px)"
    imagesrcset="
        /images/image-336x240.jpg 1x
        /images/image-672x480.jpg 2x
    "
 />
 <link
    rel="preload"
    as="image"
-    media="(min-width: 600px)"
+    media="(min-width: 600px) and (max-width: 999px)"
    imagesrcset="/images/image-480x600.jpg 2x"
 />
 <link
    rel="preload"
    as="image"
-    media="(min-width: 416px)"
+    media="(min-width: 416px) and (max-width: 599px)"
    imagesrcset="/images/image-1070x416.jpg 2x"
 />
 <link
    rel="preload"
    as="image"
+    media="(max-width: 415px)"
    href="/images/image-622x416.jpg"
 />

Cette fois-ci c’est la bonne : le navigateur va ĂȘtre capable de n’en charger qu’une seule.

Définir les attributs width et height pour régler le problÚmes de Layout Shifts

Avant de nous quitter, il y a un dernier Ă©lĂ©ment que j’aimerais que vous ayez en tĂȘte quand vous dĂ©finissez vos images. Pour cela, observons la diffĂ©rence entre les deux chargements suivants :

Petit indice, c’est particuliùrement visible à 3.3s de chargement.

En effet, sur le chargement de gauche, on voit pendant un court instant le bandeau noir de fin de page. Puis, dans les instants qui suivent, le contenu bouge un peu dans tous les sens au fur et Ă  mesure que les images arrivent. C’est ce qu’on appel du Layout Shift : un Ă©lĂ©ment s’est dĂ©placĂ© sans action de la part de l’utilisateurice.

C’est un problĂšme parce que si une personne commence Ă  lire le bandeau noir puis que les images commencent Ă  arriver, dĂ©calant ainsi le contenu, alors elle va perdre le fil de sa lecture. Il va falloir scroller – parfois trĂšs loin – jusqu’à retrouver le contenu qui l’intĂ©ressait. Pire, si jamais la personne cherche Ă  cliquer quelque part, et que juste avant de cliquer le contenu se dĂ©cale, elle pourra faire la mauvaise action, menant Ă  beaucoup de frustration.

Cela fait partie des indicateurs de performance qui sont observĂ©s par les Core Web Vitals : le CLS (Cumulative Layout Shift) et peut impacter votre SEO en plus de dĂ©teriorer l’expĂ©rience de vos utilisateurices.

Le moyen le plus simple et souvent suffisant est d’ajouter les attributs width et height sur vos images. Il s’agit de la valeur en CSS Pixels.

 <picture>
 	<source
 		media="(min-width: 1000px)"
 		srcset="
 			/images/image-336x240.jpg 1x
 			/images/image-672x480.jpg 2x
 		"
 	/>
 	<source
 		media="(min-width: 600px)"
 		srcset="/images/image-480x600.jpg 2x"
 	/>
 	<source
 		media="(min-width: 416px)"
 		srcset="/images/image-1070x416.jpg 2x"
 	/>
 	<img
 		alt="Description de l'image"
 		src="/images/image-622x416.jpg"
 		loading="lazy"
+		width="311"
+		height="208"
 	/>
 </picture>

En ajoutant ces attributs, le navigateur va ĂȘtre en mesure de comprendre l’aspect-ratio de vos images et donc rĂ©server suffisamment de place pour Ă©viter les sauts de contenus. C’est le seul changement que j’ai mis en place sur la partie droite de la vidĂ©o. Un petit changement comparĂ© au bĂ©nĂ©fice que ça apporte Ă  l’utilisateurice. Donc un bon rĂ©flexe Ă  prendre.

Récapitulatif

Maintenant que nous avons vu la thĂ©orie, qu’est-ce que ça donne en pratique ? Est-ce que mettre en place tout ça amĂ©liore rĂ©ellement les performances de nos pages ? Vous vous en doutez, si j’en vous parle, c’est que la rĂ©ponse est oui. Mais concrĂštement, j’ai mis cela en place sur toutes les images de la page d’exemple. Et grĂące Ă  cela, on passe de 2.4s de LCP Ă  1.8s, soit -25% ! 🎉

Filmstrip provenant de WebPageTest.org avant le changement des images : l'image du bandeau finit de charger Ă  2.4s
Screenshot de la vue Filmstrip dans WebPageTest.org : LCP Ă  2.4s
Filmstrip provenant de WebPageTest.org aprĂšs le changement des images : cette fois-ci l'image du bandeau fini de se charger Ă  1.8s
MĂȘme screenshot aprĂšs avoir appliquĂ© les srcset et les bons formats d'image : LCP Ă  1.8s

C’est donc rĂ©ellement quelque chose qui peut drastiquement changer la performance ressentie de vos pages.

Afin que vous puissiez bĂ©nĂ©ficier des mĂȘmes gains sur votre site, et parce qu’il est difficile de garder en tĂȘte la totalitĂ© des bonnes pratiques, voici une checklist pour rester rigoureux·se et qui pourra vous servir de pense-bĂȘte :

  • <img> normale
  • Snippet
  • <picture> et <source> dans votre HTML
  • fetchpriority
  • width & height sur la balise <img> pour Ă©viter des problĂšmes de Layout Shift (article Ă  ce sujet Ă  venir 👀)
  • Lighthouse ou WebPageTest afin de vĂ©rifier que tout est bon
  • Mastodon ou Twitter pour cĂ©lĂ©brer la victoire 🎉

Si vous avez toujours soif de Web Performance, sachez que je publie en ce moment un article par semaine Ă  ce sujet. N’hĂ©sitez donc pas Ă  me suivre sur les rĂ©seaux sociaux et Ă  partager cet article, cela contribuera Ă  me motiver Ă  tenir le rythme 😁

Pour rappel, je suis aussi disponible en freelance et peut travailler avec vous sur vos travaux en rapport avec la web performance. Que vous ayez des lenteurs déjà identifiées ou que vous ayez besoin de savoir si oui ou non la performance est un enjeux pour votre entreprise, je peux vous aider.

Dans tous les cas, la semaine prochaine nous parlerons optimisation des fonts, stay tuned !


Si vous voulez suivre mes publications, il paraĂźt que j'ai un feed RSS, Mastodon et un Twitter.

Si vous pensez à d'autres méthodes que vous voudriez que je mette en place (pigeon voyageur, avion en papier, etc.), n'hésitez pas à me les proposer :)