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.

