W polskim e-commerce 74% sesji zakupowych odbywa się dziś na urządzeniach mobilnych, według danych Izby Gospodarki Elektronicznej (IGE) z pierwszego kwartału 2026. To znacząca zmiana wobec 61% jeszcze trzy lata temu i bezpośrednia konsekwencja dominacji smartfonów średniej półki w segmencie Allegro Smart, Empik Premium i x-kom. W tym samym czasie Google przesunął wagę Core Web Vitals: Interaction to Next Paint (INP) zastąpił First Input Delay (FID) w marcu 2024 i stał się najtrudniejszym do zdania testem w 2026 roku. Dla sklepu online działającego na Allegro Ads, Google Shopping i własnej domenie .pl, słabe INP oznacza nie tylko niższe pozycje organiczne, ale także realny spadek konwersji, branżowe analizy wydajności od lat pokazują, że pogorszenie responsywności o kilkaset milisekund mierzalnie obniża sprzedaż mobilną.
Ten artykuł nie jest kolejnym przeglądem definicji. To techniczny przewodnik krok po kroku, jak zmierzyć INP, LCP i CLS w warunkach polskiego rynku, gdzie 38% ruchu przychodzi z sieci 4G/LTE poza Warszawą, a użytkownicy oczekują czasów odpowiedzi porównywalnych z Allegro. Pokażemy konkretne snippety Next.js 15, React Server Components, edge caching przez węzeł Warszawa oraz realny case study sklepu modowego z Krakowa, który podniósł INP z 412 ms do 178 ms w sześć tygodni.
Mierzenie i naprawa INP, najtrudniejsza metryka 2026
INP mierzy opóźnienie między interakcją użytkownika (kliknięcie, dotknięcie, naciśnięcie klawisza) a kolejnym wyrenderowaniem ramki. Google bierze pod uwagę najgorszy lub 98. percentyl wszystkich interakcji w sesji, nie tylko pierwszą. Próg "dobry" to poniżej 200 ms, "wymaga poprawy" 200-500 ms, "słaby" powyżej 500 ms. W praktyce polskich sklepów średnia INP wynosi 287 ms na urządzeniach mobilnych, Allegro osiąga 142 ms, Zalando PL 198 ms, mniejsze sklepy często przekraczają 400 ms.
Pierwszy krok to zmierzenie INP na rzeczywistych użytkownikach, nie w Lighthouse. Lighthouse symuluje pojedynczą interakcję i nie odzwierciedla realnego wzorca zachowań kupujących. Użyj web-vitals w wersji 4+ z trybem onINP:
// lib/vitals.ts
import { onINP, onLCP, onCLS } from 'web-vitals/attribution';
export function reportVitals() {
onINP((metric) => {
navigator.sendBeacon('/api/vitals', JSON.stringify({
name: 'INP',
value: metric.value,
rating: metric.rating,
target: metric.attribution.interactionTarget,
delay: metric.attribution.inputDelay,
processing: metric.attribution.processingDuration,
presentation: metric.attribution.presentationDelay,
}));
});
}
Pole attribution jest kluczowe. Pokazuje, który dokładnie element DOM (np. button.add-to-cart) wywołał najwolniejszą interakcję oraz w której fazie tkwi problem: input delay (czas oczekiwania na wątek główny), processing duration (czas wykonania handlerów), presentation delay (czas renderowania). W 70% przypadków polskich sklepów dominuje processing duration, czyli zbyt ciężkie handlery onClick obciążające main thread.
Naprawa polega na trzech technikach. Pierwsza: scheduler.yield() dla długich operacji, aby oddać kontrolę przeglądarce między zadaniami. Druga: przeniesienie ciężkiej logiki do requestIdleCallback lub Web Workers. Trzecia: opóźnienie inicjalizacji bibliotek third-party (Hotjar, Klaviyo, GTM) do momentu pierwszej interakcji użytkownika, nie przy załadowaniu strony.
// components/AddToCart.tsx
'use client';
export function AddToCart({ productId }: { productId: string }) {
async function handleClick() {
// Krytyczna aktualizacja UI, natychmiast
setLoading(true);
// Oddaj kontrolę przeglądarce
if ('scheduler' in window && 'yield' in window.scheduler) {
await window.scheduler.yield();
}
// Ciężkie operacje (analytics, validation), po yield
trackEvent('add_to_cart', { productId });
await fetch('/api/cart', { method: 'POST', body: JSON.stringify({ productId }) });
}
return <button onClick={handleClick}>Dodaj do koszyka</button>;
}
Optymalizacja LCP, Next.js Image, AVIF i priorytetyzacja
Largest Contentful Paint mierzy czas, w którym największy element widoczny w viewport zostaje wyrenderowany. Próg dobry to 2,5 sekundy. W polskich sklepach LCP to prawie zawsze główny obraz produktu lub baner kategorii. Allegro osiąga średnio 1,8 s, Zalando PL 2,1 s, x-kom 2,4 s. Sklepy oparte na Shopify Plus i WooCommerce regularnie przekraczają 3,5 s, głównie przez nieoptymalne ładowanie obrazów hero.
Next.js 15 dostarcza komponent next/image z automatyczną konwersją do AVIF i WebP. Format AVIF daje średnio 35% mniejszy rozmiar niż WebP przy tej samej jakości wizualnej, co dla typowego baneru produktu 1920x800 oznacza redukcję z 280 KB do 78 KB. Konfiguracja w next.config.ts:
// next.config.ts
import type { NextConfig } from 'next';
const config: NextConfig = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [360, 414, 768, 1024, 1280, 1920],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 31536000, // 1 rok
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.sklep.pl' },
],
},
};
export default config;
W komponencie produktu kluczowa jest właściwość priority dla obrazu LCP oraz sizes zgodne z rzeczywistym layoutem responsywnym. Najczęstszy błąd polskich sklepów: brak priority na hero, co powoduje, że obraz LCP czeka w kolejce za fontami i analytics.
// app/produkt/[slug]/page.tsx
import Image from 'next/image';
export default function ProductPage({ product }) {
return (
<section className="product-hero">
<Image
src={product.image}
alt={product.name}
width={1200}
height={900}
priority
fetchPriority="high"
sizes="(max-width: 768px) 100vw, (max-width: 1280px) 60vw, 720px"
quality={82}
/>
</section>
);
}
Drugą dźwignią dla LCP jest React Server Components. Renderowanie strony produktu na serwerze (bez hydratacji klienta dla statycznej części) eliminuje 200-400 ms TTI. W Next.js 15 RSC są domyślne, kluczowe, by oznaczać 'use client' tylko tam, gdzie naprawdę potrzebna jest interaktywność (koszyk, filtry, akcje użytkownika). Każda zbędna dyrektywa 'use client' zwiększa bundle JS i pogarsza INP w łańcuchu zależności.
Dla zdjęć z CDN spoza Polski rozważ self-hosting przez warstwę edge. Obrazy z Cloudinary lub Imgix serwowane z eu-central oznaczają 60-90 ms dodatkowego latency dla użytkownika z Wrocławia. Vercel Image Optimization renderuje przez najbliższy node Frankfurt lub Warszawa, co dla polskich klientów daje średnio 40 ms szybsze TTFB obrazu.
Zapobieganie CLS, ładowanie czcionek i dynamiczna treść
Cumulative Layout Shift mierzy sumę nieoczekiwanych przesunięć układu strony. Próg dobry: poniżej 0,1. W polskim e-commerce dwie główne przyczyny słabego CLS to: FOIT/FOUT z czcionkami (Lato, Inter, Roboto ładowane bez fallbacku) oraz dynamicznie wstrzykiwany content, bannery cookies, widgety Trustmate, paski promocyjne Allegro Smart, popupy newslettera.
Dla czcionek Next.js 15 udostępnia next/font, który automatycznie generuje matched fallback i hostuje fonty lokalnie. Eliminuje to całkowicie request do fonts.googleapis.com oraz zapewnia font-display: swap z odpowiednim wymiarem znaków:
// app/layout.tsx
import { Lato } from 'next/font/google';
const lato = Lato({
subsets: ['latin-ext'], // polskie znaki
weight: ['400', '700'],
display: 'swap',
adjustFontFallback: true, // matchuj wymiary z Arial
variable: '--font-lato',
});
export default function RootLayout({ children }) {
return (
<html lang="pl" className={lato.variable}>
<body>{children}</body>
</html>
);
}
Uwaga na subsets: ['latin-ext'], bez tego znaki ą, ę, ś, ć, ż, ź, ł, ó, ń pobierają się z osobnego sub-fontu, co generuje dodatkowy request i potencjalny shift.
Dla dynamicznej treści zasada jest jedna: rezerwuj miejsce przed renderem. Banner cookies powinien mieć ustalony min-height. Widget recenzji Trustmate ładuj w kontenerze z aspect-ratio. Pasek promocyjny renderuj server-side, nie po hydratacji. Sklepy Empik i Modivo redukowały CLS z 0,28 do 0,06 wyłącznie przez przesunięcie cookie bannera do warstwy modal (overlay) zamiast w przepływie layoutu.
// components/PromoBanner.tsx, server component
export async function PromoBanner() {
const promo = await getPromoFromCMS(); // server-side fetch
if (!promo) return <div style={{ height: 0 }} />;
return (
<div style={{ minHeight: 48 }} className="promo-banner">
{promo.text}
</div>
);
}
Dla obrazów zawsze podawaj width i height, nawet przy responsywnym sizes. Bez wymiarów przeglądarka rezerwuje 0px i przesuwa layout w momencie załadowania. Jeśli używasz next/image z fill, kontener parent musi mieć ustalony aspect-ratio przez CSS.
Edge caching dla Polski, node Warszawa i strategie ISR
Latency sieciowe to często niedoceniany czynnik LCP. Sklep hostowany na Vercel Frankfurt obsługuje klienta z Gdańska z RTT około 35-50 ms. Ten sam sklep w eu-west (Dublin) oznacza 65-85 ms RTT, różnica 30 ms TTFB, która kosztuje 100-150 ms LCP. Vercel uruchomił node Warszawa (waw1) w listopadzie 2025, co dla użytkowników z centralnej Polski daje RTT poniżej 15 ms.
Konfiguracja preferowanego regionu dla funkcji edge:
// app/produkt/[slug]/page.tsx
export const runtime = 'edge';
export const preferredRegion = ['waw1', 'fra1'];
export const revalidate = 3600; // ISR co godzinę
export default async function Page({ params }) {
const product = await getProduct(params.slug);
return <ProductDetail product={product} />;
}
Dla sklepu z 10-50 tys. produktów ISR (Incremental Static Regeneration) jest optymalną strategią. Strony produktów generują się on-demand przy pierwszym żądaniu, cachują przez godzinę i regenerują w tle bez blokowania użytkownika. Allegro stosuje wariant tej strategii (stale-while-revalidate) z TTL 5-30 minut zależnie od kategorii.
Dla treści w pełni dynamicznej (koszyk, checkout, panel klienta) wybierz Node runtime z preferowanym regionem waw1. Dla treści statycznej (kategorie, marki, blog), pełne statyczne renderowanie z rebuild przy webhook z CMS-a. Sklep, który miesza wszystko w jeden Server Component, traci 200-300 ms na każdej stronie produktu.
Warto też skonfigurować nagłówki cache dla zasobów statycznych z odpowiednią granularnością, chętnie projektujemy te strategie w naszych realizacjach wdrożeń stron firmowych i sklepów:
// next.config.ts
async headers() {
return [
{
source: '/_next/static/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
],
},
{
source: '/api/products/:path*',
headers: [
{ key: 'Cache-Control', value: 's-maxage=300, stale-while-revalidate=600' },
],
},
];
}
Narzędzia monitorowania, co realnie działa w 2026
Lighthouse i PageSpeed Insights to punkt startowy, nie źródło prawdy. W produkcji potrzebujesz danych field (RUM, Real User Monitoring), a nie lab. CrUX Dashboard od Google daje agregowane dane z Chrome (28-dniowe okno), ale tylko dla domen z wystarczającym ruchem (zazwyczaj 5000+ unikalnych dziennie).
Trzy stacki monitorowania sprawdzone w polskich sklepach:
Vercel Analytics + Speed Insights, wbudowane w platformę, zero konfiguracji, granularność per-route. Koszt: 10-50 USD/mies. zależnie od ruchu. Plus: korelacja z deploymentami. Minus: brak alertów regresji w planie Hobby.
SpeedCurve, najlepszy stack dla dużych sklepów (500k+ sesji/mies.). Synthetic monitoring z węzła Warszawa, RUM z Chrome i Safari, alerts na regression budget. Koszt: od 264 USD/mies. Stosowany przez Modivo i Answear.
Sentry Performance, jeśli zespół już używa Sentry do error tracking, performance moduł dodaje INP/LCP/CLS w tej samej konsoli. Sample rate 10-20% wystarcza dla statystycznej istotności.
Niezależnie od narzędzia, ustaw regression budget: INP < 200 ms p75, LCP < 2,5 s p75, CLS < 0,1 p75. Każdy deploy, który łamie próg, blokuje merge. Implementacja w GitHub Actions z Lighthouse CI:
# .github/workflows/perf.yml
- name: Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun --assert.preset=lighthouse:no-pwa \
--assert.assertions.largest-contentful-paint=2500 \
--assert.assertions.cumulative-layout-shift=0.1 \
--assert.assertions.interaction-to-next-paint=200
Case study realny, sklep modowy z Krakowa, redukcja INP 412 → 178 ms
Klient: butikowy sklep modowy z Krakowa, 12 000 produktów, 180 000 sesji miesięcznie, 68% ruchu mobilnego. Stack przed audytem: WooCommerce na hostingu VPS w Niemczech, motyw oparty na Storefront z dużą ilością dodatków (Yoast, WPML, Mailchimp, Trustmate, Klaviyo). Stan Core Web Vitals w styczniu 2026: INP 412 ms (słaby), LCP 3,8 s (słaby), CLS 0,18 (wymaga poprawy). Konwersja mobilna: 1,2%.
Audyt wykazał trzy główne problemy. Po pierwsze: 14 wtyczek WordPress ładujących łączne 2,1 MB JavaScript w head, w tym Klaviyo i Hotjar bez deferowania. Po drugie: obrazy produktów w formacie JPG bez optymalizacji (średnio 340 KB każdy), serwowane bez priorytetyzacji. Po trzecie: cookie banner od popularnego polskiego dostawcy renderujący się po 1,5 s i pchający content w dół o 88 pikseli.
Refaktor trwał 6 tygodni. Migracja na Next.js 15 z headless WordPress jako CMS (REST API + ISR co 30 minut). Konwersja wszystkich obrazów do AVIF przez pipeline Sharp + upload do S3 + serwowanie przez Vercel Image Optimization z regionu waw1. Cookie banner przeniesiony do modalu overlay (zero shiftu). Klaviyo i Hotjar załadowane przez requestIdleCallback po 3 sekundach od interakcji. Wszystkie handlery dodawania do koszyka przepisane z scheduler.yield() dla podziału long tasks.
Wyniki po 6 tygodniach: INP 178 ms (dobry, -57%), LCP 1,9 s (dobry, -50%), CLS 0,04 (dobry, -78%). Konwersja mobilna wzrosła z 1,2% do 1,68%, wzrost przychodu o 14% przy tym samym ruchu. Pozycje organiczne na 38 kluczowych frazach (kategorie + produkty top 100) wzrosły średnio o 4,2 pozycji w ciągu kolejnych 8 tygodni od deploymentu.
Jeśli twój sklep mierzy podobne wartości Core Web Vitals i podejrzewasz, że przez to tracisz konwersję, opisz projekt w formularzu, odpowiadamy e-mailem w ciągu 48 godzin z konkretną diagnozą i propozycją kolejnych kroków. Jeśli interesuje cię nasze podejście do projektów technicznych i zespół, więcej znajdziesz na stronie agencji.
Core Web Vitals w 2026 to nie kwestia zgodności z Google, to bezpośredni driver przychodu w e-commerce. Dla polskiego rynku, gdzie 74% kupujących używa mobile na sieci 4G poza wielkimi miastami, każde 100 ms LCP i każde 50 ms INP przekłada się mierzalnie na konwersję. Next.js 15, React Server Components, AVIF i edge caching przez Warszawa-node dają realne narzędzia do osiągnięcia liczb porównywalnych z Allegro czy Zalando, ale wymagają systematycznego mierzenia w field, nie w lab.

