Lazy loading

Le lazy loading, ou chargement différé, consiste à ne charger une ressource (une image, une iframe, parfois un bloc de contenu) qu'au moment où elle approche de la zone visible de l'écran, au lieu de la télécharger avec le reste de la page. Les navigateurs le prennent en charge nativement avec l'attribut loading="lazy". Bien employé, il réduit le poids du chargement initial ; appliqué à l'image principale, il dégrade le LCP, et mal implémenté, il cache des contenus aux moteurs, un cas particulier des questions traitées dans la définition du SEO JavaScript.

Définition détaillée du lazy loading

MDN définit le lazy loading comme « une stratégie qui identifie les ressources non bloquantes (non critiques) et ne les charge que lorsqu'elles sont nécessaires ». Sur une page web ordinaire, le navigateur télécharge toutes les images référencées dans le HTML dès l'analyse du document, y compris celles situées loin sous la ligne de flottaison, que l'utilisateur ne verra peut-être jamais. Le chargement différé inverse cette logique : les ressources hors écran ne sont demandées que quand l'utilisateur s'en approche en faisant défiler la page.

Le terme couvre plusieurs techniques : le chargement différé des images et des iframes, le plus courant et le seul qui dispose d'un mécanisme natif dans les navigateurs ; le découpage du code JavaScript en morceaux chargés à la demande (code splitting, imports dynamiques) ; le chargement différé de feuilles de style par requêtes media ; et, par extension, le chargement de blocs de contenu au défilement, dont le défilement infini est la forme la plus connue. Cette page traite surtout du premier cas, celui qui concerne le plus directement le référencement des images et la mesure des Core Web Vitals.

Comment fonctionne le chargement différé natif

L'attribut loading

Depuis Chrome 77, l'attribut loading des éléments <img> et <iframe> demande au navigateur de différer le chargement sans aucun script : <img src="photo.jpg" loading="lazy" width="800" height="600" alt="...">. La valeur lazy diffère le chargement jusqu'à ce que l'élément atteigne une distance calculée de la zone visible ; la valeur eager, qui est le comportement par défaut, charge immédiatement. Selon web.dev, l'attribut est pris en charge sur les images par Chrome 77, Edge 79, Firefox 75 et Safari 15.4, et sur les iframes par Chrome 77, Edge 79, Firefox 121 et Safari 16.4. Il n'y a donc plus besoin de bibliothèque JavaScript pour le cas général.

La distance de déclenchement dépend du navigateur et de la connexion. Chrome l'a abaissée en juillet 2020 : 1 250 pixels avant la zone visible sur une connexion rapide (4G), 2 500 pixels sur une connexion lente (3G ou moins), contre 3 000 et 4 000 auparavant. Les images masquées par display: none ne sont pas chargées, mais celles rendues invisibles par d'autres moyens (opacité nulle, par exemple) le sont. Depuis Chrome 121, les images d'un défilement horizontal (carrousel) obéissent aux mêmes seuils que le défilement vertical.

Deux détails de la spécification ont des conséquences pratiques, notés par MDN. Le report de chargement n'est appliqué que si JavaScript est activé, pour empêcher un site de suivre la position de défilement d'un visiteur par la simple séquence de chargement des images. Et une image différée dont les dimensions ne sont pas connues occupe zéro pixel tant qu'elle n'est pas chargée : si elle n'intersecte jamais une zone visible, elle n'est jamais chargée. Les attributs width et height sont donc indispensables, ce qui évite aussi les décalages de mise en page mesurés par le CLS.

Le chargement différé par JavaScript

Avant l'attribut natif, les bibliothèques de lazy loading remplaçaient l'attribut src par un attribut de données (data-src) et attendaient que l'image entre dans la zone visible pour le rétablir. La détection s'appuie sur l'API IntersectionObserver, qui signale au script l'entrée d'un élément dans le viewport, ou, dans les implémentations anciennes, sur des gestionnaires d'événements de défilement. Cette approche reste utile pour des cas que l'attribut natif ne couvre pas : images d'arrière-plan CSS, seuils de déclenchement personnalisés, vidéos, blocs de contenu. Elle a aussi ses pièges pour les moteurs, décrits plus bas.

Lazy loading et LCP : ne jamais différer l'image principale

Le LCP (Largest Contentful Paint) mesure le moment où le plus grand élément visible de la page est affiché ; Google demande qu'il soit inférieur à 2,5 secondes pour au moins 75 % des visites. Dans la plupart des pages, cet élément est une image : la photo d'en-tête d'un article, le visuel d'un produit. Différer son chargement revient à retarder volontairement la mesure la plus surveillée de la page, puisque le navigateur attend d'avoir analysé le document et exécuté sa logique de défilement avant de la demander. Le guide « Optimize Largest Contentful Paint » de web.dev est sans nuance : « ne jamais différer le chargement de l'image LCP, car cela entraîne toujours un délai de chargement de ressource inutile ». La recommandation inverse s'applique à cette image : la déclarer avec fetchpriority="high", éventuellement la précharger avec <link rel="preload">, et ne pas la servir par un script.

L'ampleur du problème a été mesurée. Dans une analyse publiée sur web.dev en 2022 à partir des données de HTTP Archive, Felix Arntz et Rick Viscomi relevaient que 29 % des sites utilisaient le chargement différé natif, WordPress représentant à lui seul environ 84 % de cet usage depuis qu'il l'applique par défaut, et que les pages qui l'employaient avaient un LCP médian plus lent (3 546 ms contre 2 922 ms). La cause n'était pas l'attribut lui-même mais son application aveugle à toutes les images, première image comprise. Le correctif testé, exclure l'image mise en avant et les premières images du contenu, conservait la quasi-totalité des économies de bande passante en supprimant la dégradation du LCP. WordPress a depuis intégré cette exclusion des premières images dans son cœur.

La règle qui en découle est simple : charger immédiatement (eager, ou sans attribut) tout ce qui est visible à l'ouverture de la page, sur mobile comme sur ordinateur, et différer le reste. Le premier écran ne se devine pas depuis un gabarit : il se vérifie sur les formats d'écran réels, l'image visible sur ordinateur pouvant être hors écran sur mobile, et inversement.

Lazy loading et indexation : ce que Google demande

La documentation Search Central consacre une page au sujet (« Fix lazy-loaded content »). Le principe : le contenu différé doit se charger « dès qu'il est visible dans la zone d'affichage », et jamais en réponse à une action de l'utilisateur, parce que Google Search, selon ses propres termes, n'interagit pas avec la page. Google cite trois méthodes acceptables : le chargement différé natif des images et des iframes, l'API IntersectionObserver avec son polyfill, et une bibliothèque JavaScript qui charge au passage dans le viewport. Un script qui attend un clic, un survol ou un défilement réel de l'utilisateur laisse le contenu invisible au rendu.

Pour les images, la condition d'indexation est concrète : après rendu, l'URL de l'image doit figurer dans l'attribut src de l'élément <img> (ou dans srcset, avec un src de repli). Le guide de Google sur les images précise qu'il trouve les images dans le src des éléments <img>, y compris à l'intérieur d'un <picture>, et qu'il n'indexe pas les images d'arrière-plan CSS. Une bibliothèque qui ne renseigne data-src et ne rétablit src qu'au défilement expose donc les images à ne jamais être vues par Googlebot, dont le service de rendu ne fait pas défiler la page comme un utilisateur. L'attribut natif n'a pas ce défaut : le src est présent dans le HTML dès le départ.

Pour les blocs de contenu chargés au défilement (défilement infini d'une liste d'articles ou de produits), Google demande une pagination parallèle : chaque tranche doit avoir sa propre URL stable (?page=12 plutôt qu'un paramètre relatif comme ?date=hier), les URL doivent se relier en séquence, et l'URL affichée doit être mise à jour par l'API History quand une nouvelle tranche devient la partie visible. Sans cela, seul le premier écran de la liste existe pour le moteur, et les contenus suivants ne sont ni découverts ni indexés. Les règles de la pagination s'appliquent.

Comment mettre en place et vérifier le lazy loading

  • Utiliser l'attribut natif pour les images et les iframes hors du premier écran, avec width et height sur chaque élément. Réserver une bibliothèque JavaScript aux cas non couverts, en s'assurant qu'elle s'appuie sur IntersectionObserver et qu'elle laisse une URL dans le src ou une solution de repli dans <noscript>.
  • Exclure l'image LCP et toute image visible à l'ouverture, et lui donner fetchpriority="high". Sur WordPress et les CMS qui appliquent l'attribut automatiquement, vérifier que les premières images en sont bien exemptées et corriger le gabarit sinon.
  • Différer les iframes lourdes (lecteurs vidéo, cartes, widgets sociaux) placées sous la ligne de flottaison : c'est souvent le gain le plus important en octets et en temps de traitement.
  • Vérifier le rendu côté Google avec l'outil d'inspection d'URL de la Search Console : le HTML rendu doit contenir les URL des images dans les attributs src, et l'ensemble du contenu attendu. Le test des résultats enrichis offre la même vue.
  • Mesurer avec Lighthouse et PageSpeed Insights, qui signalent à la fois les images hors écran non différées (audit « Defer offscreen images ») et l'image LCP chargée en différé ; puis contrôler sur le terrain, dans les données CrUX, que le LCP n'a pas reculé après la mise en place.

Les erreurs fréquentes

  • Différer toutes les images, y compris la première : c'est l'erreur la plus répandue et la plus coûteuse en LCP.
  • Omettre les dimensions : décalages de mise en page à chaque chargement, et images jamais chargées quand leur hauteur nulle ne rencontre pas la zone visible.
  • Conditionner le chargement à une interaction (clic sur « voir plus », survol, défilement réel) : Google ne la fera pas.
  • Laisser le src vide ou sur un pixel de remplacement sans mécanisme de repli : les images n'apparaissent pas dans le HTML rendu et ne sont pas indexées.
  • Charger au défilement une liste sans pagination : le contenu au-delà du premier écran n'existe pas pour le moteur.
  • Empiler une bibliothèque JavaScript et l'attribut natif sur les mêmes éléments : comportements contradictoires et seuils imprévisibles.

Sources :

FAQ

Le lazy loading est-il bon pour le SEO ?

Il est neutre pour le classement en lui-même, et utile pour la vitesse s'il est bien appliqué : moins d'octets au chargement initial, un premier écran affiché plus vite. Il devient nuisible dans deux cas : appliqué à l'image principale, il dégrade le LCP ; implémenté par un script qui attend une action de l'utilisateur, il cache des images ou des contenus à Google.

Faut-il encore une bibliothèque JavaScript pour le lazy loading ?

Plus pour les images et les iframes : l'attribut loading="lazy" est pris en charge par Chrome, Edge, Firefox et Safari. Une bibliothèque reste utile pour les cas particuliers (images d'arrière-plan CSS, vidéos, seuils personnalisés, blocs de contenu), à condition qu'elle s'appuie sur IntersectionObserver et laisse une URL d'image dans le HTML.

Pourquoi ne faut-il pas différer l'image LCP ?

Parce que le navigateur retarde alors la demande de l'image la plus importante de la page, au lieu de la lancer dès l'analyse du HTML. web.dev recommande l'inverse pour cette image : fetchpriority="high" et, si besoin, un préchargement. La règle vaut pour toute image visible à l'ouverture de la page.

Google voit-il les images chargées en différé ?

Oui, si l'URL de l'image se trouve dans l'attribut src (ou srcset avec un src de repli) de l'élément img après rendu. C'est le cas avec l'attribut natif. Avec une bibliothèque qui ne renseigne que data-src et attend le défilement, l'image peut rester invisible, car le service de rendu de Google ne fait pas défiler la page. L'outil d'inspection d'URL permet de le vérifier.

Le défilement infini pose-t-il un problème d'indexation ?

Oui, s'il n'est pas doublé d'une pagination. Google demande que chaque tranche de contenu ait une URL propre et stable, que ces URL soient reliées entre elles, et que l'URL affichée soit mise à jour au fur et à mesure du défilement. Sans cela, seul le premier écran est indexé.

En résumé

  • Lazy loading : ne charger une ressource (image, iframe, code, bloc de contenu) qu'au moment où elle devient nécessaire, en général à l'approche de la zone visible.
  • Natif : l'attribut loading="lazy" sur img et iframe, pris en charge par tous les navigateurs majeurs, avec width et height obligatoires ; seuils de déclenchement de 1 250 à 2 500 pixels dans Chrome.
  • LCP : ne jamais différer l'image principale ni les images visibles à l'ouverture ; les charger en priorité avec fetchpriority="high".
  • Indexation : le chargement doit se déclencher à la visibilité dans le viewport, jamais sur une action de l'utilisateur ; l'URL de l'image doit figurer dans le src après rendu ; le défilement infini exige une pagination.
  • Vérification : outil d'inspection d'URL de la Search Console pour le HTML rendu, Lighthouse et PageSpeed Insights pour les audits, données de terrain pour le LCP.

Abondance c'est aussi des audits SEO, un service de contenus et des formations en e-learning !

Lexique Abondance.com

Publié le Mis à jour le

Victor Lerat

Victor Lerat

Directeur & Consultant SEO chez Abondance

Consultant SEO, Victor Lerat dirige Abondance depuis sa reprise par le groupe KHUMBU en 2023. Co-fondateur de l'agence KHOSI à Nantes, il accompagne marques et e-commerçants sur leur visibilité dans les moteurs de recherche et les moteurs de réponse IA, avec une appétence particulière pour la donnée : logs serveur, crawl et analytics.

Les outils SEO pour vous aider