Secondo l'Osservatorio E-commerce B2C del Politecnico di Milano, nel 2025 il traffico mobile ha superato il 72% degli ingressi sui negozi online italiani, con punte oltre l'80% nel comparto moda. Eppure, analizzando i dati CrUX (Chrome User Experience Report) degli ultimi dodici mesi, oltre il 41% degli e-commerce italiani non supera la soglia "Good" su almeno una metrica dei Core Web Vitals. Il colpevole numero uno? L'Interaction to Next Paint (INP), la metrica che da marzo 2024 ha sostituito il First Input Delay e che, nel 2026, è diventata il vero killer del posizionamento per i siti transazionali.
In questa guida tecnica Le mostriamo come misurare, diagnosticare e correggere INP, LCP e CLS nel contesto specifico di un e-commerce italiano: catalogo voluminoso, traffico mobile dominante via 4G/5G eterogeneo (TIM, Vodafone, Iliad, WindTre), checkout multi-step e un'utenza che abbandona dopo 2,5 secondi di attesa percepita. Esamineremo il comportamento di player come Yoox, Esselunga online e Coin, con snippet di codice basati su Next.js 15 (App Router, React Server Components) e suggerimenti operativi per l'edge caching dal nodo di Milano.
1. Misurare e correggere INP: la metrica che penalizza l'e-commerce italiano
L'Interaction to Next Paint misura il ritardo massimo, durante una sessione, tra un'interazione utente (tap, click, keypress) e il successivo aggiornamento visivo del DOM. La soglia "Good" è 200ms; "Needs improvement" tra 200 e 500ms; "Poor" oltre i 500ms. Nei test che abbiamo condotto su un campione di 18 e-commerce italiani del settore fashion, l'INP mediano al 75° percentile si attestava a 412ms su mobile, ben dentro la zona rossa.
Le cause più frequenti sono tre: handler JavaScript pesanti sul tap del bottone "Aggiungi al carrello", rehydration di componenti React monolitici, e script di terze parti (Google Tag Manager, Klaviyo, FB Pixel) che bloccano il main thread. La buona notizia è che React Server Components in Next.js 15 risolvono strutturalmente il problema, spostando la logica non interattiva fuori dal bundle client.
Ecco un pattern concreto che applichiamo per ridurre INP sulla pagina prodotto:
// app/prodotto/[slug]/page.tsx — Server Component (zero JS al client)
import { AddToCartButton } from './AddToCartButton';
import { getProdotto } from '@/lib/catalogo';
export default async function PaginaProdotto({ params }: { params: { slug: string } }) {
const prodotto = await getProdotto(params.slug);
return (
<article>
<h1>{prodotto.nome}</h1>
<p>{prodotto.descrizione}</p>
{/* Solo questo bottone è interattivo */}
<AddToCartButton sku={prodotto.sku} />
</article>
);
}
Il componente `AddToCartButton` è l'unico marcato come `"use client"`, mentre il resto della pagina rimane HTML statico generato dal server. Risultato: il bundle JavaScript scende da una media di 280 KB gzipped (tipico di un PWA Shopify) a circa 45 KB, e l'INP misurato sul tap del CTA scende sotto i 150ms anche su dispositivi Android di fascia media (Samsung A14, Xiaomi Redmi Note 12) connessi alla 4G di Iliad in zona suburbana.
Per gli script di terze parti, la regola d'oro è il caricamento differito con `next/script` in modalità `afterInteractive` o, ancora meglio, `lazyOnload`. Su Esselunga online, ad esempio, l'esecuzione di tutti i tag pubblicitari al primo paint generava un INP di 680ms; spostandoli al primo scroll si è ridotto a 240ms. Il nostro team di sviluppo web applica questo pattern come default su ogni nuovo progetto.
2. Ottimizzare LCP con Next.js Image e formato AVIF
Il Largest Contentful Paint misura il tempo in cui l'elemento più grande del viewport diventa visibile. Su un e-commerce, è quasi sempre l'immagine principale del prodotto in alto. La soglia "Good" è 2,5 secondi; ogni 100ms in più equivalgono mediamente a un calo di conversione dello 0,3% secondo i benchmark di Akamai applicati al mercato retail europeo.
Next.js 15 fornisce il componente `<Image>` con ottimizzazioni automatiche: lazy loading nativo, generazione di srcset, conversione on-the-fly in AVIF e WebP, e priorizzazione dell'immagine above-the-fold. Ecco la configurazione raccomandata:
// next.config.ts
import type { NextConfig } from 'next';
const config: NextConfig = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [360, 414, 640, 750, 1080, 1200, 1920],
imageSizes: [16, 32, 64, 96, 128, 256, 384],
minimumCacheTTL: 31536000, // 1 anno
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.go-to-agency.com' },
],
},
};
export default config;
Sulla pagina prodotto, l'immagine LCP va marcata con `priority` per innescare il preload nel `<head>`:
import Image from 'next/image';
<Image
src={prodotto.immaginePrincipale}
alt={prodotto.nome}
width={800}
height={1000}
priority
sizes="(max-width: 768px) 100vw, 50vw"
quality={82}
/>
L'attributo `sizes` è cruciale: indica al browser quale variante del srcset scaricare prima ancora del layout. Senza, il browser scarica la risoluzione massima sprecando banda; con la configurazione corretta, un Samsung A14 in 4G scarica un AVIF da 38 KB invece di un JPEG da 240 KB, migliorando l'LCP da 3,8s a 1,9s.
Yoox, che gestisce un catalogo di oltre 4 milioni di SKU, ha investito molto nella generazione di varianti AVIF pre-compute: il loro LCP mediano su mobile è di 1,6 secondi nonostante la complessità del frontend. Coin, invece, ancora serve immagini JPEG progressive da 180 KB sulle PLP e paga il prezzo con un LCP di 3,4s al 75° percentile CrUX.
3. Prevenire CLS: font, banner cookie e contenuto dinamico
Il Cumulative Layout Shift quantifica gli spostamenti visivi imprevisti durante il caricamento. La soglia "Good" è 0,1. Tre cause dominano l'e-commerce italiano: caricamento asincrono dei web font, banner cookie GDPR che si materializzano dopo 800ms, e widget di terze parti (chat Tidio, recensioni Trustpilot, sticker promo) senza dimensioni riservate.
Per i font, Next.js 15 integra `next/font` che inline le definizioni e applica `font-display: swap` con metriche di fallback calibrate per evitare il "Flash of Unstyled Text". Esempio con Inter:
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin', 'latin-ext'],
display: 'swap',
adjustFontFallback: true,
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html lang="it" className={inter.variable}>
<body>{children}</body>
</html>
);
}
L'opzione `adjustFontFallback: true` istruisce Next.js a generare un `@font-face` di sistema con metriche `size-adjust`, `ascent-override` e `descent-override` che mimano Inter, riducendo lo shift quando il font reale entra in gioco. Su Esselunga online, dove il font `Esselunga Sans` carica a 600ms, l'attivazione di queste override ha portato il CLS da 0,18 a 0,03.
Per i banner cookie, raccomandiamo di riservare l'altezza con una classe CSS prima ancora che il widget si materializzi:
/* globals.css */
.cookie-banner-placeholder {
min-height: 84px; /* altezza esatta del banner mobile */
contain: layout style;
}
@media (min-width: 768px) {
.cookie-banner-placeholder {
min-height: 64px;
}
}
Stessa logica per le immagini: `width` e `height` espliciti sono obbligatori, anche quando si usa `fill`. Il container parent deve avere `position: relative` e dimensioni note. Coin, che inietta una sezione "Ti potrebbe interessare" dopo 1,2 secondi senza placeholder, accumula un CLS di 0,27 — fuori dal rosso solo perché l'utente normalmente ha già scrollato.
4. Edge caching dall'Italia: il nodo Milano e la latenza percepita
Il TTFB (Time To First Byte) non è ufficialmente un Core Web Vital, ma contribuisce direttamente a LCP e all'INP del primo paint. Per un utente di Roma che si collega a un origin server di Francoforte, la latenza minima è di 22ms; se l'origin è negli Stati Uniti, sale a 110ms. Moltiplicato per 3-4 round trip TLS, si parla di 400-500ms di overhead pure.
Vercel, Cloudflare e AWS CloudFront dispongono tutti di un PoP a Milano (codice MXP). La regola è semplice: tutto ciò che può essere cachato in edge, deve esserlo. Con Next.js 15 e l'App Router, basta dichiarare la strategia di rendering a livello di route:
// app/categoria/[slug]/page.tsx
export const revalidate = 3600; // ISR: rigenerato ogni ora
export const dynamic = 'force-static';
export async function generateStaticParams() {
const categorie = await getTutteCategorie();
return categorie.map((c) => ({ slug: c.slug }));
}
Per le pagine prodotto con stock dinamico, una soluzione ibrida funziona bene: HTML statico cachato in edge + componente client che fetcha lo stock real-time da un'API edge:
// app/api/stock/[sku]/route.ts
export const runtime = 'edge';
export const preferredRegion = ['fra1', 'cdg1']; // Francoforte + Parigi, vicini all'Italia
export async function GET(req: Request, { params }: { params: { sku: string } }) {
const stock = await fetch(`${process.env.WAREHOUSE_API}/stock/${params.sku}`, {
next: { revalidate: 30 },
}).then((r) => r.json());
return Response.json(stock, {
headers: {
'Cache-Control': 'public, s-maxage=30, stale-while-revalidate=300',
},
});
}
L'header `stale-while-revalidate` è la chiave: l'edge serve la risposta cached istantaneamente (TTFB inferiore a 50ms da Milano) e rivalida in background. Per un dettaglio prodotto consultato da Napoli, il primo byte arriva in 35ms invece dei 380ms di un round trip verso un origin USA. Se vuole una valutazione concreta sul Suo stack attuale, può richiedere un audit di performance al nostro team.
5. Strumenti di monitoraggio: dal lab al field
Esistono due famiglie di metriche: lab data (sintetico, riproducibile) e field data (reale, statisticamente significativo). Entrambe sono indispensabili. Ecco lo stack che raccomandiamo per un e-commerce italiano di taglia media (50k-500k visite/mese).
Lab data: PageSpeed Insights (gratis, dà entrambe le viste), WebPageTest con location "Milan, IT — Chrome — 4G" e Lighthouse CI integrato nel pipeline di deploy. Il throttling 4G simula approssimativamente una connessione mobile italiana media (15 Mbps download, 170ms RTT).
Field data: CrUX dataset (28 giorni rolling, segmentato per device), Google Search Console > Esperienza > Core Web Vitals, e un proprio Real User Monitoring tramite la libreria `web-vitals` di Google. Ecco l'integrazione minima in Next.js 15:
// app/layout.tsx
import { WebVitals } from './WebVitals';
// app/WebVitals.tsx
'use client';
import { useReportWebVitals } from 'next/web-vitals';
export function WebVitals() {
useReportWebVitals((metric) => {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
url: window.location.pathname,
});
// sendBeacon è non-bloccante, ideale per RUM
navigator.sendBeacon('/api/vitals', body);
});
return null;
}
I dati raccolti permettono di segmentare per dispositivo, ISP (TIM vs Vodafone vs Iliad) e geolocalizzazione (Nord vs Centro vs Sud). Spesso scopriamo che il 75° percentile reale è dominato da utenti del Mezzogiorno con Iliad o WindTre rurale, dove la latenza media è del 40% superiore a Milano. Questo cambia radicalmente le priorità di ottimizzazione.
6. Case study reale: outdoor retailer italiano da CLS 0,28 a 0,04
L'anno scorso abbiamo lavorato con un retailer italiano del settore outdoor (catalogo di 12.000 SKU, fatturato e-commerce di circa 4,2M€, 320k visite/mese) che presentava i tipici sintomi di un sito Magento 2 modernizzato in fretta: LCP 4,1s, INP 540ms, CLS 0,28 al 75° percentile mobile. La perdita di traffico organico era stimata in 18% anno-su-anno.
Abbiamo replatformato il frontend su Next.js 15 mantenendo Magento come backend headless via GraphQL. Tre interventi prioritari:
- LCP: migrazione tutte le immagini prodotto a AVIF/WebP servite via Vercel Image Optimization con cache di un anno, `priority` sulla prima immagine della PDP, preconnect verso il CDN delle recensioni. Risultato: LCP da 4,1s a 1,8s.
- INP: conversione di 23 componenti monolitici in React Server Components, isolamento dei widget interattivi (filtri PLP, carrello, wishlist) in client islands, deferral di GTM e Hotjar con `lazyOnload`. Risultato: INP da 540ms a 180ms.
- CLS: riserva dimensioni per banner cookie (Iubenda), font con `adjustFontFallback`, placeholder fisso per il widget Trustpilot, eliminazione di un layout shift di 120px causato da uno slider promozionale lazy. Risultato: CLS da 0,28 a 0,04.
Dopo 8 settimane di indicizzazione, i risultati di business sono stati significativi: traffico organico +27%, conversion rate mobile +14%, tempo medio sulla pagina prodotto +22%. Il CAC organico è sceso del 19% perché Google ha iniziato a posizionare il sito anche su query competitive dove prima era oltre la decima posizione.
Il messaggio è chiaro: i Core Web Vitals 2026 non sono un puro esercizio tecnico, sono il prerequisito per esistere commercialmente in SERP. Un INP a 400ms o un CLS a 0,2 oggi significa lasciare quote di mercato a competitor più diligenti. Se desidera una valutazione approfondita del Suo stack e-commerce, può descrivere il Suo progetto al nostro team di Go To Agency attraverso il modulo di richiesta preventivo: riceverà un'analisi via email entro 48 ore.
Conclusione: il triangolo INP-LCP-CLS come vantaggio competitivo
Nel 2026 la differenza tra un e-commerce italiano che cresce in organico e uno che stagnano è sempre meno una questione di link building e sempre più di esperienza utente misurabile. INP è il nuovo terreno di battaglia, LCP rimane il filtro d'ingresso, CLS è il dettaglio che separa "abbastanza buono" da "professionale". Con Next.js 15, l'edge caching dal nodo Milano e una disciplina di monitoraggio field, il triangolo è raggiungibile anche per un team interno di 2-3 sviluppatori. La curva di apprendimento è ripida, ma il ritorno sull'investimento è documentabile in trimestri, non in anni.

