Un client m'a envoyé un jour un audit d'agence avec une case rouge : « lazy loading non détecté sur les images de la page d'accueil ». Verdict de l'audit : -12 points de performance, correction estimée à une journée de dev. J'ai ouvert son site. Sa page d'accueil contenait exactement une image. Une seule. En haut, en pleine largeur, celle qu'on voit avant tout scroll. L'agence voulait la charger en différé. C'est-à-dire la faire disparaître au moment précis où elle compte le plus.
Ce n'est pas une anecdote isolée. Depuis que tout le monde a découvert l'attribut loading="lazy", on le pose sur tout ce qui bouge, parfois même sur ce qu'il ne faut surtout pas toucher. Et en 2026, avec le LCP sous surveillance rapprochée, ce réflexe coûte des points de classement à ceux qui le prennent à la légère.
Le lazy loading reste une excellente pratique. Mais c'est une pratique conditionnelle. Voici comment je l'applique sur mes projets aujourd'hui, y compris les cas où je l'évite volontairement.
Points clés à retenir
- Ne jamais différer l'image du haut de page (above the fold) : c'est le LCP qui trinque.
- L'attribut natif
loading="lazy"suffit dans 90 % des cas. Intersection Observer reste utile pour du contrôle fin ou du chargement déclenché. - Toujours déclarer
widthetheight(ou unaspect-ratio) pour éviter le CLS. - Le format compte autant que le déclencheur : AVIF ou WebP avant de parler de lazy loading.
- Méfiez-vous des librairies qui promettent « un lazy loading parfait » : la plupart cassent le SEO de l'image ou chargent une dépendance de 40 ko pour rien.
Pourquoi un lazy loading d'images mal fait dégrade vos performances
L'idée du lazy loading est simple : charger une image seulement quand elle approche du viewport. Sur une page catalogue avec 60 vignettes, ça évite de télécharger 4 Mo d'un coup alors que l'utilisateur ne regardera peut-être que les six premières. Sur le papier, c'est du gagnant-gagnant : moins de bande passante, moins de travail pour le navigateur au chargement initial.
Le problème arrive quand on l'applique à l'aveugle. Le navigateur doit alors attendre d'être proche du bas de page pour demander l'image, recevoir la réponse, la décoder, puis l'afficher. Sur la première image visible, ce délai se transforme directement en retard de rendu. Et ce retard, Google le mesure comme votre LCP.
Le LCP, cette métrique qui ne pardonne pas
Le LCP (Largest Contentful Paint) mesure le moment où le plus gros élément visible de la page s'affiche. Sur la grande majorité des sites, cet élément est une image. Un budget correct se situe sous les 2,5 secondes. Or, si l'image héros est en lazy loading, vous ajoutez un aller-retour réseau supplémentaire au pire moment : celui où l'utilisateur attend le plus.
Bon, soyons clairs : la différence n'est pas toujours dramatique. Sur une connexion fibre, l'écart peut se limiter à 200-400 ms. Sur un mobile en 4G dégradée, ça peut grimper à plus d'une seconde. Sur un site d'e-commerce, cette seconde se traduit directement en taux de rebond.
Le seuil de déclenchement : le vrai levier
Personne ne vous le dit dans les tutos de base, mais le comportement du lazy loading dépend entièrement de quand le navigateur décide de charger. Avec l'attribut natif, ce seuil n'est pas paramétrable : le navigateur applique ses propres règles, généralement une marge de quelques centaines de pixels autour du viewport, variable selon la vitesse de connexion estimée. Sur Chrome récent, cette marge est généreuse, ce qui atténue le problème du LCP. Sur d'autres moteurs, c'est moins prévisible.
C'est précisément là qu'Intersection Observer prend le relais quand vous avez besoin de contrôle. Vous pouvez définir un rootMargin explicite, par exemple 600px ou même 200% de la hauteur du viewport, pour déclencher le chargement anticipé. Dans mes tests, un rootMargin de 600 à 800 px offre un bon compromis : les images arrivent avant que l'utilisateur ne les voie, sans pour autant tout charger à l'ouverture.
À retenir : le natif suffit pour le bas de page, l'Observer devient intéressant quand vous voulez maîtriser finement la distance d'anticipation.
Quand utiliser le lazy loading, et quand l'éviter absolument
La règle que j'applique est bête, mais elle m'a évité bien des ennuis : tout ce qui peut apparaître sans scroll ne doit jamais être différé. Le reste, oui.
- L'image héros, le logo, la première vignette d'une grille, la bannière produit : chargement normal, avec
fetchpriority="high"sur l'élément critique. - Les images situées en dessous de la ligne de flottaison : lazy loading, systématiquement.
- Les iframes et vidéos intégrées plus bas dans la page : lazy loading également, mais attention aux embeds qui gèrent mal le différé.
- Les images des carrousels : cas délicat, on en parle plus bas.
Le piège des carrousels et des onglets
Un carrousel affiche par défaut une seule image, mais en contient cinq, dix, parfois vingt. Si vous lazy-load tout le lot, les slides invisibles ne se chargent jamais, et au clic suivant, l'utilisateur voit un flash blanc le temps que l'image arrive. Moche.
La solution : charger la première slide en priorité normale, et différer les suivantes avec un seuil large, ou mieux, les précharger une fois la première affichée. Même logique pour les onglets et les accordéons : si un contenu caché est en lazy loading, il se chargera à l'ouverture, avec un délai perceptible. Pour ces cas, je préfère souvent désactiver le différé et anticiper le chargement en arrière-plan.
Le cas des images sous la ligne de flottaison mobile
Sur mobile, le viewport est étroit et haut. Résultat : ce qui est « en dessous » sur desktop peut très bien se retrouver visible sur un écran de téléphone. Si vous codez en pensant desktop, vous risquez de différer une image que le visiteur mobile voit immédiatement. C'est l'erreur que j'ai commise sur un site de recettes : la photo principale passait sous le pli sur mon écran de test, mais s'affichait au-dessus sur un petit iPhone. Le LCP mobile a souffert pendant deux semaines avant que je ne comprenne.
La parade : testez toujours sur un vrai petit écran, ou utilisez l'émulation mobile du navigateur avec les dimensions d'un appareil récent.
Lazy loading natif ou Intersection Observer : lequel choisir en 2026
Le débat n'est plus vraiment « natif contre librairie ». La question est de savoir quand l'attribut HTML ne suffit plus.
| Approche | Quand l'utiliser | Points de vigilance |
|---|---|---|
loading="lazy" natif | Images sous la ligne de flottaison, cas standards | Seuil non paramétrable, comportement variable selon le navigateur |
| Intersection Observer | Besoin de contrôle du seuil, chargements conditionnels, animations liées | Nécessite du JS, à intégrer avec précaution pour ne pas bloquer le rendu |
| Librairies tierces | Cas très spécifiques, projets legacy | Poids JS ajouté, dépendance externe, souvent redondant avec le natif |
Lazy loading + srcset | Images responsives, plusieurs résolutions | Bien vérifier que le bon fichier est choisi selon le viewport |
Mon avis, que j'assume : dans la quasi-totalité des projets, le natif suffit. J'ai arrêté d'installer des librairies de lazy loading il y a deux ans, et mes scores n'ont pas baissé. Au contraire, j'ai gagné quelques dizaines de kilo-octets de JS à chaque fois.
L'interaction avec le CLS
Vous pouvez avoir un lazy loading impeccable et un CLS catastrophique. Le CLS (Cumulative Layout Shift) mesure les décalages de mise en page pendant le chargement. Si vos images n'ont pas de dimensions réservées dans le HTML, elles apparaissent en poussant le contenu vers le bas, et ça se voit.
La règle est simple et ancienne : déclarez toujours width et height sur vos balises <img>, ou bien utilisez aspect-ratio en CSS. Le navigateur peut alors réserver l'espace avant même que l'image n'arrive. Peu importe que la valeur soit exacte : le ratio est ce qui compte. Une image 1600×900 peut être déclarée en 16/9, le navigateur s'en contentera.
Sans cette précision, le lazy loading aggrave le CLS, puisque l'image manquante laisse un trou qui se comble brutalement à l'arrivée.
Le format et la compression : le levier avant le différé
Je vais être direct : si vos images pèsent 800 ko chacune, aucun lazy loading ne vous sauvera. Le meilleur gain de performance que j'ai obtenu sur un site client venait simplement de la conversion en AVIF. La page est passée de 3,2 Mo à 680 ko, sans toucher une ligne de JavaScript.
L'ordre d'action que je recommande :
- Convertir les images en WebP (fallback générique) et proposer de l'AVIF quand le support le permet.
- Redimensionner les fichiers à la taille réellement affichée, pas à la résolution du fichier source.
- Mettre en place un
srcsetavec plusieurs largeurs. - Ensuite, ajuster le comportement de chargement.
Faire du lazy loading sur des images lourdes, c'est déplacer le problème, pas le résoudre. Vous chargez moins souvent, mais chaque chargement reste coûteux.
Servir le bon fichier selon le viewport
Un piège fréquent : déclarer un srcset et un lazy loading qui finissent par servir la grande image au mobile. Le navigateur choisit la source selon la largeur du viewport au moment où il déclenche le chargement. Si votre différé se déclenche trop tard, le choix peut se faire dans de mauvaises conditions, et vous téléchargez une version surdimensionnée pour rien. Ce n'est pas dramatique, mais c'est du gaspillage silencieux.
Testez avec l'onglet réseau du navigateur en conditions mobiles pour vérifier quel fichier est réellement téléchargé.
Ce que je fais systématiquement sur mes projets
Pour finir, voici la liste courte que j'applique aujourd'hui, sans exception :
- Image héros : chargement normal,
fetchpriority="high", jamais de différé. - Toutes les autres images :
loading="lazy", avec dimensions réservées. - Format AVIF avec fallback WebP, dimensionnement à la taille d'affichage.
- Intersection Observer uniquement quand j'ai besoin d'un seuil précis.
- Test sur un vrai mobile, pas seulement dans l'émulateur.
Et cette fameuse page d'accueil avec une seule image dont je vous parlais au début ? J'ai retiré l'attribut lazy que l'agence voulait ajouter, mis le fetchpriority="high", converti l'image en AVIF. Le LCP est passé de 3,1 s à 1,4 s. Résultat : aucun lazy loading ajouté, et des scores nettement meilleurs.
C'est un peu l'ironie de la chose. Dans ma boîte à outils, le lazy loading est un outil puissant. Mais les meilleurs résultats viennent souvent de ce qu'on décide de ne pas faire. La prochaine fois qu'un audit vous suggère d'en ajouter partout, posez-vous une seule question : est-ce que cette image s'affiche avant ou après le premier scroll ? Votre réponse vaut mieux que n'importe quelle recommandation générique.