Salta al contenuto principale

03 - LCP Optimization: Image Preloading, Critical CSS e SSR

LCP Optimization: Image Preloading, Critical CSS e SSR Il Largest Contentful Paint (LCP) misura il tempo necessario perche il contenuto principale della...

Cosa ne pensi di questo articolo?

03 - LCP Optimization: Image Preloading, Critical CSS e SSR
Core Web VitalsFoto:Bibek ghoshPexels

In Breve

LCP Optimization: Image Preloading, Critical CSS e SSR Il Largest Contentful Paint (LCP) misura il tempo necessario perche il contenuto principale della...

LCP Optimization: Image Preloading, Critical CSS e SSR

Il Largest Contentful Paint (LCP) misura il tempo necessario perche il contenuto principale della pagina sia visibile all'utente. Per Google, la soglia "Good" e 2.5 secondi: oltre questa soglia, il ranking organico viene penalizzato. Secondo i dati CrUX di marzo 2026, il 35% dei siti web ha ancora LCP nel range "Needs Improvement" o "Poor", spesso per ragioni evitabili con interventi mirati.

L'elemento LCP e tipicamente l'immagine hero, il banner principale, o il blocco di testo piu grande above the fold. La strategia di ottimizzazione dipende dal tipo di elemento: un'immagine richiede approcci diversi rispetto a un testo. In entrambi i casi, ci sono quattro tecniche ad alto impatto che coprono il 90% dei casi reali: preloading dell'immagine LCP, inlining del Critical CSS, preloading dei font con font-display: swap, e Server-Side Rendering.

Cosa Imparerai

  • Come identificare l'elemento LCP con Chrome DevTools e Lighthouse
  • Image preloading con <link rel="preload"> e l'attributo fetchpriority="high"
  • Critical CSS: cosa e, come estrarlo e come includerlo inline nell'HTML
  • Font preloading con font-display: swap e preconnect
  • Come SSR (Server-Side Rendering) migliora strutturalmente il LCP
  • Come misurare il miglioramento LCP prima e dopo le ottimizzazioni

Identificare l'Elemento LCP

Il primo passo e sempre sapere quale elemento e il tuo LCP. Puoi identificarlo in due modi:

  • Chrome DevTools: nel pannello Performance, dopo una registrazione del caricamento pagina, cerca il marcatore "LCP" nella timeline. Cliccaci sopra per vedere quale elemento ha triggerato l'LCP e quante risorse dipendenti ha richiesto.
  • web-vitals.js: la property entries dell'oggetto metric LCP contiene la LargestContentfulPaint entry con l'elemento DOM corrispondente.
import { onLCP } from 'web-vitals';

onLCP((metric) => {
  const lcpEntry = metric.entries[metric.entries.length - 1];
  console.log('Elemento LCP:', lcpEntry.element);
  console.log('Valore LCP:', metric.value, 'ms');
  console.log('Rating:', metric.rating); // 'good' | 'needs-improvement' | 'poor'
});

Strategia 1: Image Preloading con fetchpriority

Se il tuo elemento LCP e un'immagine, il miglioramento piu impattante e il preloading esplicito con alta priorita. Per default, il browser scopre le immagini solo quando il parser HTML incontra il tag <img>, che puo avvenire molto dopo che il documento ha iniziato a caricarsi. Con <link rel="preload">, il browser inizia a scaricare l'immagine il prima possibile.

<!-- Nel <head> dell'HTML, prima di altri tag -->
<link
  rel="preload"
  as="image"
  href="/images/hero.webp"
  fetchpriority="high"
  imagesrcset="/images/hero-400.webp 400w,
               /images/hero-800.webp 800w,
               /images/hero-1200.webp 1200w"
  imagesizes="100vw"
/>

L'attributo fetchpriority="high" (Baseline 2023, 92% supporto) comunica al browser che questa risorsa e critica e deve essere scaricata prima delle risorse con priorita normale. Su reti lente, questo puo fare la differenza di 0.5-1.5 secondi sull'LCP.

Immagini Responsive: imagesrcset e imagesizes

Se usi immagini responsive con srcset, devi aggiungere imagesrcset e imagesizes al link di preload per fare in modo che il browser precarichi la variante corretta per il viewport corrente, non la versione piu grande.

Formato WebP e AVIF

L'ottimizzazione del formato immagine e complementare al preloading. WebP riduce tipicamente il peso delle immagini del 25-35% rispetto a JPEG. AVIF riduce del 50-55% ma ha tempi di decodifica piu alti su hardware meno recente. Per l'immagine LCP, WebP e il formato ottimale nel 2026, con AVIF per un progressivo miglioramento su browser moderni.

<picture>
  <source
    srcset="/images/hero.avif"
    type="image/avif"
  />
  <source
    srcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w"
    type="image/webp"
    sizes="100vw"
  />
  <img
    src="/images/hero.jpg"
    alt="Hero image"
    width="1200"
    height="600"
    fetchpriority="high"
    loading="eager"
  />
</picture>

Non usare loading="lazy" sull'elemento LCP

loading="lazy" ritarda il download dell'immagine finche non e vicina al viewport. Per l'elemento LCP, che e per definizione gia nel viewport, questo causa un ritardo inutile. Usa loading="eager" (o nessun attributo loading) per l'immagine LCP. Applica loading="lazy" solo alle immagini below the fold.

Strategia 2: Critical CSS Inline

Per default, il browser non inizia a renderizzare la pagina finche non ha scaricato e parsato tutti i CSS linkati nell'<head>. Se il tuo file CSS e grande (100KB+) e serve su una CDN lenta, questo puo aggiungere 0.5-2 secondi all'LCP. La soluzione e il Critical CSS: estrarre solo i CSS necessari per renderizzare il contenuto above the fold e includerli inline nell'HTML.

<!-- INVECE DI: -->
<link rel="stylesheet" href="/styles/main.css">

<!-- USA: -->
<style>
  /* Critical CSS inline - solo quello necessario above the fold */
  body { margin: 0; font-family: sans-serif; }
  .hero { width: 100%; height: 60vh; background: #f5f5f5; }
  .hero h1 { font-size: 2.5rem; color: #333; }
  nav { display: flex; justify-content: space-between; padding: 1rem; }
</style>

<!-- Carica il CSS completo in modo non-bloccante -->
<link
  rel="preload"
  as="style"
  href="/styles/main.css"
  onload="this.rel='stylesheet'"
/>
<noscript><link rel="stylesheet" href="/styles/main.css"></noscript>

Strumenti per Estrarre il Critical CSS

Estrarre manualmente il Critical CSS non e praticabile su siti grandi. Usa strumenti automatici:

  • Critters (plugin webpack/Vite): estrae e inline il CSS critico automaticamente
  • critical (npm): tool CLI per estrarre il critical CSS da qualsiasi URL
  • PurgeCSS + Critters: combinazione ideale per ridurre il CSS totale prima dell'inline
// vite.config.ts - con Critters per Critical CSS automatico
import { defineConfig } from 'vite';
import critters from 'vite-plugin-critters';

export default defineConfig({
  plugins: [
    critters({
      // Inline il critical CSS, carica il resto in modo asincrono
      strategy: 'critical',
      // Non rimuovere i CSS non usati (lascialo a PurgeCSS)
      pruneSource: false,
    }),
  ],
});

Strategia 3: Font Preloading con font-display: swap

I web font possono bloccare il rendering del testo (FOIT - Flash of Invisible Text) fino a 3 secondi su connessioni lente, ritardando l'LCP se il testo e l'elemento LCP. Due ottimizzazioni si combinano per eliminare questo problema:

1. font-display: swap dice al browser di mostrare il testo immediatamente con il font di fallback, poi sostituirlo con il font web quando e disponibile. Elimina il FOIT.

/* In @font-face o nel parametro di Google Fonts */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap; /* Critico per LCP */
  font-weight: 400 700;
}

/* Con Google Fonts: aggiungi &display=swap all'URL */
<link
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
  rel="stylesheet"
/>

2. Preconnect + Preload riduce la latenza di scoperta e download del font:

<!-- Preconnect: stabilisce la connessione con il font server in anticipo -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- Preload: scarica il file font prima che il CSS venga parsato -->
<link
  rel="preload"
  as="font"
  type="font/woff2"
  href="/fonts/inter-400.woff2"
  crossorigin
/>

Strategia 4: Server-Side Rendering

Le quattro strategie precedenti ottimizzano le risorse; SSR ottimizza la struttura fondamentale della pagina. In un'applicazione SPA (Single Page Application), il browser riceve HTML quasi vuoto e deve scaricare, parsare ed eseguire JavaScript per renderizzare il contenuto. Con SSR, il browser riceve HTML gia renderizzato: l'elemento LCP e immediatamente visibile senza aspettare il JavaScript.

Il miglioramento LCP con SSR e tipicamente di 0.5-2 secondi:

  • SPA senza SSR: HTML shell → JS download (300-800ms) → JS execution (100-500ms) → API calls (200-800ms) → render → LCP
  • Con SSR: HTML completo → LCP immediato (solo il tempo di download dell'HTML e il TTFB del server)

Framework come Next.js, Angular Universal (SSR), Nuxt.js e SvelteKit includono SSR nativo. Per Angular, la configurazione e gestita da angular.json e dal server Express:

// angular.json - abilita SSR
{
  "projects": {
    "app": {
      "architect": {
        "build": {
          "configurations": {
            "production": {
              "server": "src/main.server.ts",
              "prerender": false,    // SSR on-demand
              "ssr": true
            }
          }
        }
      }
    }
  }
}

Misurare il Miglioramento LCP

Dopo aver implementato le ottimizzazioni, misura il miglioramento con questi strumenti:

  1. Lighthouse CI: crea baseline prima delle ottimizzazioni e confronta dopo. Esegui piu run (5+) e usa la mediana per ridurre la varianza.
  2. WebPageTest.org: ottieni filmstrip e waterfall dettagliato del caricamento su dispositivi reali e connessioni reali.
  3. web-vitals.js in produzione: monitora il P75 dell'LCP reale degli utenti per 7-14 giorni dopo il deploy.
import { onLCP } from 'web-vitals';

// Monitora LCP in produzione
onLCP({ reportAllChanges: true }, (metric) => {
  // Invia solo il valore finale (quando la pagina e in background)
  if (metric.entries.length > 0) {
    navigator.sendBeacon('/api/vitals', JSON.stringify({
      metric: 'LCP',
      value: metric.value,
      rating: metric.rating,
      url: window.location.pathname,
      device: navigator.connection?.effectiveType || 'unknown',
    }));
  }
});

Impatto Combinato delle Quattro Strategie

Applicare tutte e quattro le strategie in combinazione su una SPA tipica con immagine hero, web fonts e CSS bundled produce miglioramenti reali:

  • Image preloading + fetchpriority: -300-600ms LCP
  • Critical CSS inline: -200-500ms LCP (elimina render-blocking)
  • Font preloading + font-display:swap: -100-300ms LCP
  • SSR: -500-2000ms LCP (dipende dalla complessita della SPA)

Il risultato totale puo portare un LCP da 4-6 secondi a 1.5-2.5 secondi, passando da "Poor" a "Good" in modo consistente.

Conclusioni

L'ottimizzazione del LCP e un'area dove pochi interventi mirati producono impatti enormi. Il 35% dei siti con LCP nel range "Needs Improvement" o "Poor" ha quasi certamente uno o piu di questi quattro problemi: immagine hero non precaricata, CSS che blocca il rendering, font che ritardano il testo, o SPA senza SSR.

Il workflow corretto e: 1) misura il LCP reale con web-vitals.js, 2) identifica l'elemento LCP, 3) analizza il waterfall del caricamento per trovare il collo di bottiglia, 4) applica le ottimizzazioni rilevanti, 5) verifica il miglioramento con Lighthouse CI e dati di campo.

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti