Lazy loading fatto bene: quando rimandare le immagini e quando è un errore

Il caricamento differito riduce i dati trasferiti, ma sull’immagine sbagliata peggiora l’LCP: come funziona loading=”lazy”, quali immagini differire, come si comporta WordPress e come verificarlo.

Una griglia di immagini: la prima riga caricata sopra una linea tratteggiata gialla, le righe sottostanti come segnaposto tratteggiati in attesa, con l’attributo loading lazy

Il lazy loading, il caricamento differito delle immagini, è una delle ottimizzazioni più note e più semplici da applicare. Il principio è intuitivo: perché scaricare subito decine di immagini che l’utente vedrà solo scorrendo, o forse mai? Rimandarle libera banda e risorse per ciò che serve davvero al primo sguardo, e su pagine lunghe il risparmio in dati trasferiti può essere enorme.

Proprio perché è così semplice, però, viene spesso applicato a tutto, senza distinzione. E applicato all’immagine sbagliata, il lazy loading non accelera la pagina: la rallenta, e peggiora proprio la metrica che più dipende dalle immagini, il Largest Contentful Paint. In questo articolo vediamo come funziona il caricamento differito nativo, quando usarlo, quando evitarlo e come WordPress lo gestisce.

Come funziona il lazy loading nativo

Per anni il caricamento differito ha richiesto librerie JavaScript che sostituivano l’attributo src con un data-src e lo ripristinavano quando l’immagine si avvicinava allo schermo. Oggi tutti i browser principali supportano l’attributo nativo loading, per immagini e iframe:

<img src="galleria-1.webp" width="800" height="600" loading="lazy" alt="…">
<iframe src="https://www.youtube-nocookie.com/embed/…" loading="lazy" title="…"></iframe>

Con loading="lazy" il browser non scarica l’immagine finché non si trova entro una certa distanza dallo schermo. Non aspetta che sia visibile: la carica con un margine di anticipo, così da averla pronta quando l’utente ci arriva scorrendo. In Chrome questo margine è di circa 1250 pixel su connessioni veloci e di circa 2500 pixel su quelle lente, dove serve più tempo. Il valore predefinito, loading="eager", corrisponde al comportamento normale: scaricare subito.

Schema di una pagina su smartphone: sopra la piega l’immagine hero con caricamento immediato e fetchpriority high, logo e prima foto con caricamento immediato predefinito; sotto la piega galleria, prodotti correlati e piè di pagina con loading lazy, caricati quando entrano nel margine di anticipo
Caricamento immediato sopra la piega, differito sotto: il browser anticipa le immagini differite prima che diventino visibili.

Il vantaggio della soluzione nativa rispetto alle librerie è notevole. Non serve JavaScript, quindi non c’è codice da scaricare ed eseguire; il browser conosce la velocità della connessione e adatta il margine; e l’immagine resta un normale elemento img con il suo src, leggibile da qualsiasi strumento, compresi i motori di ricerca.

L’errore più comune: l’immagine LCP differita

Il problema nasce quando loading="lazy" finisce sull’immagine più importante della pagina: la foto di apertura, l’immagine principale di una scheda prodotto, la copertina di un articolo. Quasi sempre è l’elemento LCP, e differirla ha un effetto preciso. Normalmente il browser scopre le immagini mentre legge l’HTML, grazie al preload scanner, e le richiede subito, in parallelo con fogli di stile e script. Un’immagine differita, invece, non può essere richiesta finché il browser non sa dove si trova nella pagina, cioè finché non ha scaricato il CSS e calcolato il layout. Solo allora scopre che è visibile e ne avvia il download.

Confronto tra due caricamenti: con l’immagine hero in loading lazy, il download parte solo dopo HTML, CSS e layout e l’LCP arriva a 3,1 secondi; con fetchpriority high l’immagine viene scaricata subito in parallelo all’HTML e l’LCP scende a 1,5 secondi
Un’immagine differita viene richiesta solo dopo il layout: sull’elemento LCP significa perdere tutto il vantaggio del caricamento parallelo.

Il ritardo che ne risulta può facilmente superare il secondo su mobile, e si somma direttamente all’LCP. Lighthouse lo segnala con un avviso specifico quando l’immagine LCP è stata caricata in modo differito, ed è uno dei suggerimenti più frequenti nei report dei siti WordPress. La correzione è semplice: togliere loading="lazy" da quell’immagine e, anzi, darle la massima priorità con fetchpriority="high", come spiegato nell’articolo sull’LCP.

Con le librerie JavaScript il danno è ancora maggiore. Un’immagine con data-src non è un’immagine per il preload scanner: il browser non la vede fino a quando lo script non è stato scaricato, eseguito e ha calcolato la posizione degli elementi. Sull’immagine LCP questo può significare secondi di ritardo, e per questo le librerie di lazy loading andrebbero sostituite dall’attributo nativo, oppure configurate per escludere le immagini sopra la piega.

Quali immagini differire e quali no

La regola generale è semplice da enunciare: caricare subito ciò che è visibile al primo sguardo, differire il resto. Nella pratica, la piega non è una linea fissa. Cambia con il dispositivo, con l’orientamento dello schermo, con la presenza di banner o avvisi sui cookie, con la lunghezza dei titoli. Un’immagine che su desktop è sotto la piega può essere visibile su un tablet, e viceversa. Alcuni criteri aiutano a decidere.

  • Mai differire l’elemento LCP. La foto principale, la copertina, la prima immagine di prodotto: caricamento immediato e, per la più importante, fetchpriority="high".
  • Non differire le immagini piccole sopra la piega. Logo, icone, avatar nell’intestazione: pesano poco e differirle non porta alcun vantaggio, mentre può farle comparire in ritardo.
  • Differire tutto ciò che è chiaramente più in basso. Gallerie, immagini nel corpo di un articolo dopo i primi paragrafi, prodotti correlati, loghi dei partner nel piè di pagina, mappe e video incorporati.
  • Nei casi dubbi, ragionare sul mobile. È lì che si misurano le Core Web Vitals più critiche, ed è lì che la piega è più alta: sullo schermo di uno smartphone, spesso solo la prima immagine è visibile.

Le griglie meritano attenzione. In una pagina di categoria con quattro prodotti per riga su desktop e due su mobile, le immagini della prima riga dovrebbero essere caricate subito e le altre differite. Nei caroselli, la prima diapositiva va trattata come un’immagine sopra la piega, le successive possono essere differite, ma solo se il carosello non le rende visibili automaticamente dopo pochi secondi.

Dimensioni esplicite: il lazy loading senza CLS

Un’immagine differita arriva dopo che la pagina è già stata disegnata. Se il browser non sa in anticipo quanto spazio occuperà, al suo arrivo sposterà il contenuto sottostante, generando CLS. Per questo ogni immagine, differita o no, deve avere gli attributi width e height, o un aspect-ratio nel CSS: il browser riserva lo spazio corretto e l’immagine lo riempie senza spostare nulla. Le immagini differite sono quelle più esposte al problema, perché arrivano quando l’utente sta già leggendo o scorrendo.

Un segnaposto, come un colore di sfondo coerente con l’immagine o una versione sfocata e leggerissima, migliora la percezione durante lo scorrimento veloce, quando l’immagine non ha fatto in tempo ad arrivare. Deve però occupare esattamente lo stesso spazio e non deve richiedere JavaScript pesante per funzionare.

Oltre le immagini: iframe, sfondi e rendering

Gli iframe sono spesso i candidati migliori per il caricamento differito, perché un video incorporato o una mappa trascinano con sé centinaia di kilobyte di JavaScript. loading="lazy" sugli iframe funziona in tutti i browser principali; per i video, una facciata che mostra solo l’anteprima e carica il lettore al clic è ancora più efficace, come abbiamo visto parlando degli script di terze parti.

Le immagini di sfondo definite nel CSS non supportano l’attributo loading. Se sono sopra la piega vanno trattate con attenzione, perché il browser le scopre solo dopo aver letto il CSS: quando sono l’elemento LCP, conviene trasformarle in un vero elemento img o precaricarle con <link rel="preload">. Se sono sotto la piega, possono essere assegnate con una classe applicata tramite Intersection Observer.

Esiste anche una forma di differimento che riguarda il rendering invece del download: la proprietà CSS content-visibility: auto permette al browser di saltare il calcolo del layout e del disegno delle sezioni fuori schermo finché non si avvicinano. Su pagine molto lunghe e complesse può ridurre il lavoro iniziale, ma va accompagnata da contain-intrinsic-size per evitare salti della barra di scorrimento.

Quanto si risparmia davvero

Il beneficio del caricamento differito dipende dalla lunghezza della pagina e dal comportamento degli utenti. Su un articolo con quindici immagini, di cui la maggior parte dei lettori vede solo le prime tre o quattro, il risparmio può superare il megabyte per visita: meno dati per chi naviga con un piano limitato, meno banda occupata durante il caricamento iniziale, meno traffico sul server o sulla CDN. Su una pagina corta con due immagini, il vantaggio è trascurabile e il rischio di sbagliare l’immagine da differire è proporzionalmente più alto.

C’è poi un effetto meno evidente ma importante: le immagini non visibili che vengono scaricate subito competono per la banda con quelle visibili e con le risorse critiche. Anche se il browser assegna loro una priorità bassa, su una connessione mobile limitata dieci download simultanei rallentano quello che conta. Differirle significa lasciare la strada libera all’immagine LCP, ai fogli di stile e ai font.

Nelle pagine con scorrimento infinito, come alcuni elenchi di prodotti o archivi di articoli, il lazy loading è indispensabile, ma non basta: ogni nuovo blocco di contenuti aggiunge elementi al DOM e lavoro al browser. Oltre certe dimensioni conviene una paginazione tradizionale, che offre anche indirizzi stabili per i motori di ricerca, o un pulsante esplicito per caricare altri risultati, che lascia all’utente il controllo e rende il comportamento prevedibile.

Come si comporta WordPress

WordPress aggiunge loading="lazy" automaticamente alle immagini dei contenuti dalla versione 5.5 e agli iframe dalla 5.7. Nelle prime versioni lo applicava a tutte le immagini, compresa la prima, con il risultato di peggiorare l’LCP di molti siti. Dalla versione 6.3 il comportamento è più intelligente: il core stima quali immagini sono probabilmente sopra la piega, non le differisce e aggiunge fetchpriority="high" a quella che ritiene essere l’elemento LCP.

La stima si basa su una soglia: per impostazione predefinita, le prime tre immagini dei contenuti vengono caricate senza differimento. Il valore si può cambiare con il filtro wp_omit_loading_attr_threshold, e conviene farlo quando si conosce la struttura del tema. Sulle pagine di questo sito, per esempio, negli articoli l’unica immagine sopra la piega è la copertina, e la soglia è impostata a uno: tutte le immagini del corpo vengono differite.

// Negli articoli solo la copertina è sopra la piega
add_filter(
	'wp_omit_loading_attr_threshold',
	function ( $threshold ) {
		return is_singular( 'post' ) ? 1 : $threshold;
	}
);

La stima del core funziona bene con i temi che usano le funzioni standard per le immagini, ma può sbagliare con costruttori di pagine, slider e blocchi personalizzati che generano il markup in modo proprio. A questo si aggiungono i plugin di ottimizzazione che applicano il proprio lazy loading, spesso basato su JavaScript, sovrapponendosi al core. Il risultato va sempre verificato sulla pagina reale: controllare nel codice sorgente quale immagine ha fetchpriority="high", quali hanno loading="lazy", e confrontarlo con l’elemento LCP indicato da PageSpeed Insights.

Lazy loading e SEO

Il lazy loading nativo non crea problemi di indicizzazione: Googlebot vede l’attributo src e carica le immagini normalmente. Diverso è il caso delle soluzioni JavaScript che caricano i contenuti solo in risposta allo scorrimento: il crawler non scorre la pagina come un utente, e contenuti che compaiono solo dopo un evento di scroll rischiano di non essere visti. Con l’attributo nativo, o con Intersection Observer, il problema non si pone.

Come verificare

Tre controlli bastano per la maggior parte dei siti. In PageSpeed Insights o Lighthouse, verificare quale sia l’elemento LCP e che non compaia l’avviso sul caricamento differito dell’immagine LCP. Nel pannello Network dei DevTools, ricaricare la pagina senza scorrere e controllare che vengano scaricate solo le immagini visibili, più quelle nel margine di anticipo. Infine, scorrere rapidamente su una connessione lenta simulata e osservare se le immagini compaiono in tempo e se il contenuto resta fermo mentre arrivano.

In sintesi

Il lazy loading è uno strumento efficace se applicato con criterio: riduce i dati trasferiti e lascia banda alle risorse importanti, ma sull’elemento sbagliato ritarda proprio ciò che l’utente deve vedere per primo. La regola è caricare subito ciò che è sopra la piega, con la priorità più alta per l’elemento LCP, e differire tutto il resto, sempre con dimensioni esplicite per evitare spostamenti del layout.

L’attributo nativo loading ha reso superflue quasi tutte le librerie JavaScript, e WordPress lo gestisce automaticamente con una stima ragionevole delle immagini sopra la piega. Ma la stima resta una stima: conoscere la struttura delle proprie pagine, regolare la soglia e verificare il risultato sul campo è ciò che distingue un lazy loading che accelera da uno che, senza che nessuno se ne accorga, rallenta.

IL PROSSIMO PASSO

Partiamo
dal tuo sito.

Raccontaci il progetto, i problemi che riscontri e i tuoi obiettivi. Definiremo insieme un intervento su misura.

Richiedi un’analisi

Ti porta al modulo della pagina Contatti.

MANAGED SERVER S.R.L.info@corewebvitals.it +39 02 5656 9681

Lunedì – venerdì · 09:30 – 19:30

Preferisci un modulo online?
Contattaci su managedserver.it ↗