# Lazy loading delle immagini, misurato

Con 24 immagini da 1600 px il caricamento eager trasferisce 3,86 MB e finisce in 18,9 s; con loading=lazy 4 KB e 261 ms. Ma il primo schermo non va mai messo lazy: cosa dicono i numeri.

> Pagina: https://ui.eneawork.it/blog/performance-immagini-lazy
> Markdown: https://ui.eneawork.it/blog/performance-immagini-lazy.md

*2026-11-01*

Su una galleria di 24 immagini da 1600×1000 px il lazy loading ha portato i byte al load da 3.858.349 a 4.078 e il load da 18.864 a 261 ms. Il primo schermo però non va mai reso lazy: nella stessa prova l'immagine più grande è arrivata a 3.552 ms, dopo lo scroll.

Tre pagine identiche con le stesse 24 immagini JPEG (3.847.431 byte su disco), servite da un server statico locale e misurate tre volte ciascuna in Chrome 155 headless con puppeteer: rete Slow 4G simulata (1,6 Mbps, 150 ms di RTT), CPU a 4x, e uno scroll scriptato fino in fondo dopo il load. Eager: 25 richieste, 3.858.349 byte, load 18.864 ms. Tutto lazy: 1 richiesta, 4.078 byte al load, load 261 ms; dopo lo scroll 12 immagini e 1.925.929 byte. Hero eager più resto lazy: 643.104 byte al load, load 3.436 ms. Comando: node scripts/perf-lab.js http://localhost:3200/img-lazy.html 3 con SCROLL=1. Il catalogo misura 111.985 componenti, 1.556 blocchi e 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026.

## Cosa cambia con loading=lazy

La riga che conta è la seconda: 4 KB contro 3,86 MB. Il browser scarica solo ciò che sta per entrare nel viewport, e chi legge la prima schermata non paga le altre 23 immagini. La terza riga mostra il costo del margine di precaricamento: con un'immagine eager il browser ha già chiesto circa 640 KB al load.

## Il codice che serve

width e height riservano lo spazio e tengono il CLS a zero nella nostra prova. fetchpriority="high" sul primo elemento visibile dice al browser di non rimandarlo.

## Perché il primo schermo resta eager

Nella variante tutta lazy l'LCP misurato è 3.552 ms contro 300 ms della variante eager: l'immagine più grande è entrata nel viewport solo con lo scroll scriptato, quindi il paint più grande è arrivato tardi. Il lazy loading sposta i byte, non li elimina, e metterlo sull'hero sposta il problema dentro l'LCP.

## Quando il lazy non serve

## Il limite da dichiarare

La prova gira su un server locale con 150 ms di latenza simulata e immagini mai in cache. Su una connessione reale il load eager non arriva a 18,8 s, e con HTTP/2 le 24 richieste costano meno di quanto sembri. Lo scroll scriptato carica solo 12 immagini su 24: i byte totali dipendono da quanto la persona scorre, che è esattamente il punto.

## Dove continuare

Il catalogo EneUI indicizza 111.985 componenti, per esempio shape-landing-hero, e il blocco blog-01: un hero con immagine piena è il caso in cui il lazy va escluso. Leggi quando il lazy loading peggiora le cose, cosa peggiora l'LCP e come si fissa un performance budget.

## FAQ

### loading=lazy va su tutte le immagini?

No. Va su tutto ciò che sta sotto la piega; l'immagine principale del primo schermo resta eager, meglio con fetchpriority=high.

### Il lazy loading migliora l'LCP?

Solo se l'elemento LCP non è lazy. Applicato all'immagine più grande rimanda il paint e nella nostra prova ha portato l'LCP da 300 a 3.552 ms.

### Serve anche decoding=async?

Aiuta a non bloccare il thread durante la decodifica, ma non sostituisce width e height: senza dimensioni il layout si sposta comunque.

### Perché il browser carica immagini che non ho ancora visto?

Chrome precarica le immagini entro una certa distanza dal viewport, che dipende dalla velocità stimata della rete. Per questo la variante hero ha già chiesto 643 KB al load.

### Il lazy si misura sul campo?

Sì, con l'API PerformanceObserver sui layout shift e sul Largest Contentful Paint. Il laboratorio dice cosa succede in un caso, il campo dice quanto scorrono le persone.
