Une image de 480 Ko qui bloque le rendu d'une page pendant 1,2 seconde. Voilà ce que j'ai trouvé en ouvrant le rapport Lighthouse d'un client, il y a quelques mois. Le site affichait une belle image de bannière, des vignettes de produits, un carrousel. Et le navigateur téléchargeait tout ça avant même d'afficher le premier pixel de texte. Le lazy loading était « activé ». Enfin, sur le papier.
Parce que le lazy loading, c'est comme les régimes : tout le monde en parle, peu le font correctement. Et un lazy loader mal réglé peut transformer une page rapide en une page qui clignote, saute et fait fuir vos visiteurs. Dans mon cas, ce client perdait près de 40 % de son score de performance à cause de trois images mal configurées. Trois.
Je vais vous montrer comment optimiser le chargement paresseux des images pour de vrai, avec les chiffres, les pièges, et les erreurs que j'ai commises moi-même.
Points clés à retenir
- Le lazy loading natif (
loading="lazy") suffit dans 80 % des cas, mais jamais pour l'image LCP. - Sans
widthetheightsur vos balisesimg, vous créez un décalage de mise en page qui pénalise votre score. - Une marge de préchargement mal calibrée fait charger les images trop tard : l'utilisateur voit un trou blanc.
- Les attributs
srcsetetsizescombinés au lazy loading sont souvent mal associés, et le navigateur télécharge la mauvaise taille. - Mesurer avant/après, sinon vous ne saurez jamais si votre optim css et vos images ont réellement servi à quelque chose.
Le lazy loading des images : comment ça marche vraiment (et pourquoi ça rate souvent)
Le principe est simple. Une image n'est téléchargée que lorsqu'elle approche de la zone visible de l'écran. Avant, elle reste en attente. On économise de la bande passante, on raccourcit le chemin critique de rendu, et le contenu important s'affiche plus vite.
Sur mon propre blog, j'ai appliqué loading="lazy" à toutes les images d'un coup. Résultat : le temps de chargement initial est passé de 2,8 secondes à 1,4 seconde. Génial, non ? Sauf que l'image de couverture, celle que Google utilise comme indicateur LCP, s'affichait désormais après 3 secondes. J'avais amélioré le global en dégradant le visible. Erreur de débutant.
Pourquoi l'image LCP ne doit jamais être différée
Le LCP (Largest Contentful Paint) mesure le moment où le plus gros élément visible apparaît. Si cet élément est une image et que vous la mettez en lazy, le navigateur attend que l'utilisateur défile pour la charger. Sauf que l'utilisateur, lui, ne défile pas : il regarde le haut de la page. Résultat, votre LCP explose.
La règle que j'applique désormais sans exception : tout ce qui est visible au premier écran se charge en priorité, avec loading="eager" et éventuellement fetchpriority="high" sur l'image principale. Le reste, en dessous, passe en lazy. Simple. Efficace.
Le natif suffit-il, ou faut-il passer par IntersectionObserver ?
Franchement, pour 90 % des sites, loading="lazy" natif fait le job. Il est pris en charge par tous les navigateurs modernes, ne nécessite aucun JavaScript, et fonctionne même quand le script plante.
Mais il a une limite : vous ne contrôlez pas la distance de préchargement. Le navigateur décide. Sur une connexion lente, cela peut produire des images qui apparaissent avec un délai visible. Dans ce cas, un IntersectionObserver avec une rootMargin de 200 à 400 pixels permet de déclencher le chargement un peu avant que l'image n'entre dans l'écran. J'ai testé cette approche sur un site e-commerce avec beaucoup de vignettes : le temps d'affichage perçu est passé sous la barre des 100 ms, contre 300-400 ms avec le natif seul.
Le revers de la médaille : plus de code, plus de maintenance, et un risque de bug si vous gérez mal les images déjà chargées lors d'un retour arrière dans le navigateur.
Les pièges techniques qui plombent vos performances (même avec un lazy loader)
Voici les erreurs que je vois le plus souvent. Et que j'ai moi-même commises, avouons-le.
L'oubli des dimensions width et height
Une image sans dimensions réservées provoque un décalage de mise en page (CLS). Le navigateur réserve zéro espace, l'image arrive, tout saute. Avec le lazy loading, ce phénomène est amplifié, car l'image arrive encore plus tard.
La solution : toujours indiquer width et height sur vos balises img, et laisser le CSS gérer le ratio via aspect-ratio ou height: auto. En deux lignes de CSS, j'ai réglé un problème de CLS qui faisait perdre 0,15 point à un site. Sur un score de 100, c'est énorme.
Srcset, sizes et lazy loading : une combinaison fragile
Quand vous utilisez srcset et sizes, le navigateur choisit la source la plus adaptée. Mais si vous ajoutez du lazy loading par-dessus, il peut sélectionner une image trop petite, car il ne connaît pas encore la taille réelle de l'élément. Résultat : une image floue qui se charge vite, mais qui gâche l'expérience.
Mon conseil : définissez des valeurs sizes réalistes et testez sur un vrai appareil. Pas sur votre écran 4K avec fibre. Sur un téléphone en 4G, dans un train.
Les placeholders LQIP sont-ils vraiment utiles ?
Le LQIP (Low Quality Image Placeholder) affiche une version floue très légère en attendant l'image finale. Joli sur le papier. Dans les faits, cela ajoute une requête réseau supplémentaire, du code CSS, et parfois un effet de flou qui dure plus longtemps que le chargement réel. Sur un projet, j'ai supprimé les placeholders et gagné 300 ms sur le temps d'affichage des vignettes. Comme quoi, l'optimisation, c'est parfois enlever des choses.
Tableau comparatif des approches de lazy loading
| Approche | Mise en place | Contrôle du préchargement | Impact sur le LCP | Idéal pour |
|---|---|---|---|---|
loading="lazy" natif | Une ligne de code | Limité (décision navigateur) | Neutre si bien utilisé | Blogs, sites vitrines, la majorité des cas |
| IntersectionObserver | JavaScript, 20-40 lignes | Total (rootMargin réglable) | Peut améliorer si bien calibré | Galeries, pages avec beaucoup d'images |
| Bibliothèques (lazysizes, etc.) | Dépendance externe | Bon, avec options | Variable | Projets legacy, compatibilité ancienne |
| Aucun lazy loading | Rien | Aucun | Mauvais si beaucoup d'images | Pages avec très peu d'images |
Ce tableau n'est pas exhaustif, mais il donne une idée. En règle générale, commencez par le natif. Si vous avez un besoin spécifique, passez à l'IntersectionObserver. Les bibliothèques, c'est pour les cas particuliers ou les vieux projets.
Optimisation au-delà du lazy loading : ce qui compte vraiment
Le lazy loading ne fait pas tout. Une image de 2 Mo en PNG restera lourde, même chargée intelligemment. J'ai vu des sites appliquer un lazy loading parfait sur des images de 3 Mo. Le résultat : des images qui arrivent tard et qui pèsent une tonne. C'est comme mettre une ceinture de sécurité sur une voiture sans freins.
Formats, compression et taille adaptée
Convertissez en WebP ou AVIF. Divisez le poids par deux, parfois plus. Sur un de mes projets, le passage de JPG à WebP a réduit le poids total des images de 65 %. Le temps de chargement complet de la page est passé de 3,1 à 1,8 seconde. Sans toucher au lazy loading.
Utilisez aussi des tailles adaptées. Pas besoin d'une image de 2000 px de large pour une vignette de 300 px. Les CDN d'images (Cloudinary, imgix, ou même les solutions intégrées de votre hébergeur) gèrent cela automatiquement.
L'optimisation CSS, un levier souvent oublié
Le CSS peut lui-même ralentir l'affichage des images. Un fichier CSS trop lourd, des sélecteurs complexes, ou des animations coûteuses sur les images peuvent retarder leur rendu. J'ai déjà passé une après-midi à chercher pourquoi une image mettait 500 ms de plus que prévu, pour finalement découvrir un box-shadow animé sur un conteneur parent. Le CSS n'était pas le coupable direct, mais il ajoutait une charge de calcul inutile.
Quelques réflexes :
- Minifiez et compressez vos fichiers CSS.
- Évitez les animations sur les propriétés qui déclenchent un recalcul de mise en page.
- Chargez le CSS critique en priorité, le reste en différé.
Sur un site que j'ai audité, la simple suppression d'une police d'icônes inutilisée a réduit le temps de rendu de 200 ms. Le lazy loading était déjà en place. C'est dire si l'optimisation est un travail d'ensemble.
Comment mesurer l'efficacité de votre lazy loading
Vous ne pouvez pas améliorer ce que vous ne mesurez pas. Et j'ai trop souvent vu des développeurs activer le lazy loading puis passer à autre chose, sans jamais vérifier le résultat.
Trois outils que j'utilise systématiquement :
- Lighthouse (intégré à Chrome DevTools) pour un premier diagnostic rapide.
- PageSpeed Insights pour avoir une idée des performances réelles sur mobile, même si l'outil a ses limites.
- WebPageTest pour tester dans des conditions réseau réalistes (3G, 4G, latence élevée).
Ce que vous devez regarder : le LCP, le CLS, et le nombre total de requêtes d'images au chargement initial. Si vous voyez encore 15 images se charger d'un coup alors que seules 3 sont visibles, c'est qu'il y a un problème.
Un détail qui m'a échappé pendant longtemps : les images en background-image dans le CSS ne sont pas concernées par loading="lazy". Elles se chargent quand le CSS est interprété. Si vous avez des bannières en background, elles plombent votre chargement initial sans que vous le sachiez.
Faut-il adopter le lazy loading partout ?
Non. Et c'est probablement le point le plus important de cet article.
Le lazy loading est une optimisation conditionnelle. Sur une page avec trois images, il n'apporte rien. Sur une page avec une seule image (une page produit, par exemple), il peut même nuire. Sur un site avec cinquante vignettes, il est indispensable.
Mon approche actuelle : je l'active par défaut sur toutes les images qui ne sont pas dans le premier écran, et je désactive explicitement pour celles qui le sont. C'est simple, prévisible, et ça fonctionne. Pas besoin de sur-ingénierie.
Et si vous hésitez, testez. Prenez une page, mesurez avant, activez le lazy loading, mesurez après. Si le gain est nul ou négatif, désactivez. Le but n'est pas d'avoir un score parfait sur un outil, c'est que vos visiteurs voient vos images rapidement.
Ce qu'il faut retenir de tout ça
Le lazy loading n'est pas une baguette magique. C'est un outil parmi d'autres, qui donne le meilleur de lui-même quand il est combiné à des images légères, des dimensions réservées, et une mesure rigoureuse.
La prochaine fois que vous ouvrez un rapport de performance et que vous voyez une image de 1,5 Mo qui bloque tout, posez-vous la bonne question : est-ce que cette image doit vraiment se charger maintenant ? Si la réponse est non, vous savez quoi faire. Et si vous ne savez pas, commencez par le plus simple : loading="lazy" sur tout ce qui est sous la ligne de flottaison, et rien d'autre.
Le reste, c'est du réglage. Et du réglage, on en fait tous les jours.