Différer les images hors écran sur mobile : guide du lazy loading natif
Lazy loading natif, content-visibility, et pourquoi différer les images en JavaScript est un poids mort

Différer les images sur mobile : le standard
Les pages mobiles se disputent des connexions et un CPU limités. Chaque image hors écran que vous chargez d'emblée vole de la bande passante aux images et scripts qui comptent réellement pour l'affichage initial.
Dernière révision par Arjen Karel en mars 2026

Table of Contents!
1. Différer les images hors écran sur mobile : lazy loading natif
Lorsqu'un navigateur charge une page, il ouvre un nombre limité de connexions parallèles (cela dépend de nombreux facteurs, mais 6 par domaine est une moyenne courante). Si ces connexions sont utilisées pour télécharger des images hors écran (par exemple, un logo en pied de page ou une diapositive de carrousel), le téléchargement des ressources critiques (généralement l'image du LCP, les scripts importants et les polices) sera en concurrence pour obtenir des emplacements et de la bande passante. C'est ce qu'on appelle la congestion réseau, et cela dégrade directement vos Core Web Vitals.
En différant les images hors écran à l'aide de l'attribut natif loading, vous donnez la priorité à ce qui compte. Le navigateur ne récupère que ce qui est immédiatement visible, réservant la bande passante aux ressources qui impactent le Largest Contentful Paint (LCP) et le First Contentful Paint (FCP). Le lazy loading natif délègue cette priorisation au mécanisme propre du navigateur, ce qui est plus rapide et élimine le besoin de bibliothèques de lazy loading en JavaScript.
Implémentation
Pour toutes les images situées sous la zone d'affichage initiale (« la ligne de flottaison »), ajoutez l'attribut loading="lazy".
<!-- Image différée standard -->
<img src="product-detail.jpg"
loading="lazy"
alt="Vue latérale du châssis"
width="800"
height="600"
decoding="async">
Les attributs width et height sont essentiels. Sans eux, le navigateur ne peut pas réserver d'espace avant le chargement de l'image, ce qui provoque un layout shift (CLS). 62 % des pages mobiles omettent encore de définir des dimensions explicites sur au moins une image.
Comment le lazy loading fonctionne sur mobile : l'heuristique du navigateur
Le lazy loading natif est supérieur aux solutions JavaScript car le navigateur ajuste le seuil de chargement (le moment où le téléchargement d'une image est déclenché) en fonction du type de connexion effectif (ECT).
- En 4G/WiFi : le moteur Blink (Chrome/Edge) utilise un seuil prudent d'environ 1 250 px. Il suppose une faible latence et ne récupère l'image que lorsque l'utilisateur s'en approche relativement près en défilant.
- En 3G/Slow-2G : le seuil s'étend à environ 2 500 px. Le navigateur lance la requête beaucoup plus tôt pour compenser les temps d'aller-retour élevés, de sorte que l'image soit prête avant que l'utilisateur ne la fasse défiler dans la vue.
Selon le Web Almanac 2025, la page mobile médiane charge 15 images totalisant 911 Ko. Seulement environ 26 % de ces images utilisent loading="lazy". Les autres se chargent de manière immédiate (eagerly), se disputant les mêmes connexions limitées. Sur une connexion mobile 4G typique, cela signifie que l'image du LCP reste bloquée en attente derrière une douzaine d'images que l'utilisateur ne verra pas avant plusieurs secondes.
Exception critique : le candidat LCP
Une régression de performance courante : appliquer loading="lazy" à l'élément du Largest Contentful Paint (généralement l'image hero). Cela retarde la récupération jusqu'à ce que la mise en page soit terminée.
Les recherches de Google montrent que le lazy-loading de l'image du LCP ajoute 624 ms au LCP médian. Ce n'est pas un risque théorique. 17 % des pages mobiles commettent encore cette erreur selon le Web Almanac 2025. Si Lighthouse signale cela, consultez comment corriger l'avertissement de LCP en lazy-loaded.
L'image du LCP doit être chargée immédiatement (eager-loaded) et priorisée :
<!-- Image Hero : immédiate et priorisée -->
<img src="hero.jpg"
alt="Collection d'été"
width="1200"
height="800"
loading="eager"
fetchpriority="high">
Ne combinez pas loading="lazy" avec fetchpriority="high". Ils se contredisent : lazy dit au navigateur d'attendre, high lui dit de se dépêcher. Le navigateur ignore l'indication de priorité lorsque lazy est défini. Pour en savoir plus sur la façon dont les navigateurs priorisent les ressources, consultez le guide de priorisation des ressources.
2. Complexités sur mobile : zone d'affichage et tactile
Les zones d'affichage (viewports) sur mobile ne sont pas statiques. La zone visible change à mesure que l'utilisateur fait défiler, tourne l'appareil, ou déclenche la rétraction de la barre d'URL. C'est là que le lazy loading natif présente un réel avantage par rapport aux solutions JavaScript.
- La zone d'affichage (Viewport) : la zone rectangulaire visible de la fenêtre du navigateur. Sur mobile, celle-ci est dynamique ; elle change de dimensions en fonction de l'orientation de l'appareil (portrait ou paysage) et de l'état de l'interface du navigateur (rétraction des barres d'URL).
- La ligne de flottaison (The Fold) : le bord inférieur exact de la zone d'affichage. C'est le seuil qui sépare le contenu visible du contenu hors écran.
- Au-dessus de la ligne de flottaison : tout contenu visible immédiatement au chargement de la page sans faire défiler. Les images à cet endroit sont critiques et ne doivent jamais être en lazy-loaded.
- En dessous de la ligne de flottaison : tout contenu situé verticalement après la ligne de flottaison. Ce contenu est non critique et doit être différé jusqu'à ce que l'utilisateur s'en approche en faisant défiler.

La zone d'affichage dynamique
Sur les navigateurs mobiles, la hauteur de la zone d'affichage (vh) est fluide. Lorsque l'utilisateur initie un défilement tactile, la barre d'URL et les contrôles de navigation se rétractent souvent, modifiant la taille de la zone visible.
Les bibliothèques de lazy loading JavaScript calculent généralement la hauteur de la zone d'affichage (window.innerHeight) une seule fois au chargement de la page. Lorsque les navigateurs mobiles redimensionnent dynamiquement la zone visible en masquant la barre d'URL lors d'un défilement, les méthodes JavaScript continuent d'utiliser l'ancienne valeur de hauteur, plus petite. Les images restent non chargées même lorsqu'elles entrent dans la zone d'affichage étendue, causant des espaces vides pour les visiteurs.
Le moteur de rendu interne du navigateur suit automatiquement la zone d'affichage visuelle, de sorte que les déclencheurs du lazy loading natif se déclenchent indépendamment des changements de taille de la zone d'affichage. C'est l'une des raisons de préférer le lazy loading natif à toute alternative en JavaScript.
3. Décodage d'images sur mobile et limitation du CPU
Les appareils mobiles ont un CPU limité et le décodage d'images sur mobile peut être relativement lent et coûteux. Convertir un JPEG en bitmap nécessite de nombreux cycles de CPU. Sur un processeur mobile, le décodage d'une séquence de grandes images peut bloquer le thread principal pendant 50 ms à 100 ms chacune, causant de la latence d'entrée (input latency).
La solution : content-visibility
La propriété CSS content-visibility: auto agit comme un rendu paresseux (lazy rendering). Elle demande au navigateur d'ignorer entièrement les phases de mise en page (layout) et de peinture (painting) pour les éléments hors écran. L'élément existe dans le DOM, mais n'existe pas dans l'arbre de rendu (Render Tree) tant qu'il ne s'approche pas de la zone d'affichage.
Comme cette optimisation fonctionne en ignorant le rendu de l'arborescence d'un élément, vous ne pouvez pas l'appliquer directement à une balise <img> (qui n'a pas d'arborescence). Appliquez content-visibility au conteneur de produit ou à la carte d'image qui héberge les images et son contenu :
@media (max-width: 768px) {
.image-card, .product-card {
/* Ignore le rendu du conteneur et de ses enfants */
content-visibility: auto;
/* Essentiel : Empêche le conteneur de se réduire à 0px de hauteur */
contain-intrinsic-size: auto 300px;
}
}
Cela garantit que même si une image est téléchargée, le navigateur ne paie pas le coût de mise en page/peinture tant que l'utilisateur ne fait pas défiler la page jusqu'à elle.
content-visibility a atteint le statut Baseline en septembre 2024 lorsque Safari 18 a apporté sa prise en charge. Elle fonctionne désormais sur 93 % des navigateurs dans le monde. Les benchmarks de Google montrent une augmentation des performances de rendu par 7 au chargement initial pour les pages comportant de nombreuses sections hors écran.
Si vous souhaitez vérifier l'amélioration du rendu sur des appareils réels, le Real User Monitoring vous montrera l'impact réel sur l'INP et le LCP sur l'ensemble de votre trafic mobile. Sur les sites surveillés par CoreDash, les pages utilisant content-visibility: auto sur les grilles de produits affichent un INP environ 15 % meilleur sur mobile par rapport aux pages sans cette propriété.
4. Méthodologies obsolètes : pourquoi les éviter
Avant que loading="lazy" ne soit pris en charge par les navigateurs, JavaScript était la seule option. Avec un lazy loading natif atteignant 95 % de prise en charge mondiale, ces méthodes en JavaScript sont devenues de la dette technique. Supprimez-les.
L'ère du gestionnaire de défilement (2010 à 2016)
Les premières implémentations attachaient des écouteurs d'événements à l'événement de défilement (scroll event).
// Obsolète : ne pas utiliser
window.addEventListener('scroll', () => {
images.forEach(img => {
if (img.getBoundingClientRect().top < window.innerHeight) {
img.src = img.dataset.src;
}
});
});
Blocage du thread principal : l'événement de défilement se déclenche des dizaines de fois par seconde. L'exécution de la logique et le calcul de la mise en page (getBoundingClientRect) lors d'un défilement actif provoquent des pertes d'images (saccades ou jank).
Layout Thrashing : interroger des propriétés géométriques force le navigateur à recalculer de manière synchrone la mise en page, une opération coûteuse en calcul sur les processeurs mobiles.
L'ère de l'IntersectionObserver (2016 à 2019)
L'API IntersectionObserver a amélioré les performances en observant de manière asynchrone les changements de visibilité des éléments.
// Obsolète : préférez le loading="lazy" natif lorsque cela est possible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
Dépendance aux scripts : cela nécessite l'exécution de JavaScript. Si le thread principal est occupé à hydrater un framework (React/Vue), les images restent non chargées même si elles se trouvent dans la zone d'affichage.
Absence de conscience du réseau : contrairement au chargement natif, IntersectionObserver utilise des marges fixes (par exemple, rootMargin: '200px'). Il n'étend pas automatiquement sa mémoire tampon (buffer) sur les réseaux lents, ce qui entraîne des flashs blancs pour les utilisateurs ayant de mauvaises connexions.
Pour un aperçu complet des techniques d'optimisation d'images au-delà du lazy loading, ou pour en savoir plus sur la façon de différer les images d'arrière-plan CSS (ce que loading="lazy" ne couvre pas), consultez ces guides dédiés.
I have done this before at your scale.
Complex platforms, large dev teams, legacy code. I join your team as a specialist, run the performance track, and hand it back in a state you can maintain.
Discuss Your Situation
