INP (Interaction to Next Paint) — deep dive 2026
INP (Interaction to Next Paint) zastąpił FID w marcu 2024 jako Core Web Vital responsywności. INP mierzy worst-case opóźnienie interakcji w całej sesji — nie tylko pierwszej. Cel <200ms na mobile. Wysokie INP = ranking penalty + frustrated users. W 2026 to jeden z najważniejszych ranking factors, ale w audytach, które robimy, wciąż też najmniej rozumiany — klienci pytają o LCP, prawie nikt nie pyta o INP, dopóki nie pokażemy, gdzie boli.
TL;DR — INP
| Element | Detale |
|---|---|
| Co mierzy | Czas od interakcji (kliknięcie) do następnego painta |
| Granice | Good <200ms, Needs Improvement 200-500ms, Poor >500ms |
| Wprowadzony | Marzec 2024 (zastąpił FID) |
| Mobile vs desktop | Mobile 2-3x wyższe INP (słabszy CPU) |
| Ranking factor | Tak — Page Experience update |
| Główne przyczyny | Heavy JavaScript, long tasks, inefficient handlers |
| Optymalizacja | Code splitting, defer, web workers, optimize handlers |
Czym INP różni się od FID?
FID (First Input Delay) — deprecated
Mierzył opóźnienie pierwszej interakcji użytkownika. Problem: jeśli pierwszy klik był szybki (50ms), ale każdy następny 800ms — FID nadal pokazywał „good". To była metryka, która pozwalała stronie wyglądać dobrze na papierze, mimo że realny użytkownik po piątym kliknięciu miał już dość.
INP (Interaction to Next Paint) — current
Mierzy interakcje w całej sesji. Bierze 75th percentile (lub max przy <50 interakcji). Pokazuje realny user experience.
Praktyka:
- FID: 1 measurement
- INP: aggregated z 10+ interakcji
- INP > FID typowo (FID was forgiving)
Co to jest „Next Paint"?
User klika button → strona musi:
- Process event (event listener fires)
- Run JavaScript (callback execution)
- Update DOM (state change)
- Re-render (browser paints new state)
INP = czas od step 1 do step 4 (next paint po interakcji).
Granice INP
Good (<200ms)
- Interakcja czuje się instant
- User nie zauważa opóźnienia
- Ranking factor passing
Needs Improvement (200-500ms)
- Interakcja czuje się laggy
- User zauważa opóźnienie
- Ranking factor warning
Poor (>500ms)
- Interakcja czuje się broken
- User clicka multiple times (więcej events!)
- Ranking penalty significant
Dlaczego INP jest hard?
Cztery rzeczy sprawiają, że INP jest trudniejsze do ogarnięcia niż LCP czy CLS — choć w naszym korpusie audytów zdecydowanie częściej trafiamy na układ odwrotny niż ten, którego wszyscy się spodziewają. INP praktycznie nigdy nie jest metryką, która oblewa. Wracamy do tego przy punkcie drugim niżej, bo to zmienia kolejność diagnozy.
1. Mobile devices are slower
iPhone 12 vs MacBook M2 — INP 5-10x wyższe na mobile. Devs testują na M2, mobile users sufferują. To najczęstszy powód, dla którego klient jest zaskoczony wynikiem audytu — na jego laptopie strona „działa świetnie".
2. Real user data
Lab tests (Lighthouse) nie capture real INP. CrUX (Chrome User Experience Report) data przeważa.
Konkretny pomiar, który dobrze to ilustruje. U firmy B2B z branży testowania oprogramowania dane polowe CrUX pokazały INP rzędu kilkudziesięciu do stu kilkudziesięciu milisekund — FAST zarówno na mobile, jak i na desktopie, czyli grubo poniżej progu 200 ms. Ta sama strona miała jednocześnie TTFB i LCP powyżej 4,5 sekundy na desktopie, oba sklasyfikowane jako SLOW. Interakcja była szybka, strona była wolna. Wąskie gardło siedziało po stronie serwera, nie wątku głównego — i żadne cięcie JavaScriptu by tego nie ruszyło. Stąd teza, którą warto zapamiętać przed lekturą reszty tego tekstu: INP to metryka, którą najczęściej diagnozuje się fałszywie. Zanim zaczniesz ciąć JavaScript, sprawdź dane polowe.
3. Long tail
INP = worst case. 99% interakcji może być szybkie, 1 slow click rejestruje INP.
4. Third-party scripts
Analytics, chat, ads — często źródło long tasks. Hard to remove (business need). To jest ten rodzaj problemu, gdzie techniczna odpowiedź (usuń skrypt) zderza się z biznesową (marketing potrzebuje danych z tego skryptu) — i trzeba szukać kompromisu, nie tylko fixu w kodzie.
Główne przyczyny wysokiego INP
1. Heavy JavaScript
Główna przyczyna. Frameworks (React, Vue, Angular) + business logic + tracking = 200-500KB+ JS execution.
Fix:
- Code splitting (load tylko tego co potrzebne)
- Lazy loading components
- Tree shaking (usuń unused code)
- Smaller bundle (Astro, Next.js + RSC)
2. Long tasks (>50ms)
Browser blocks UI gdy JS task running. Long tasks = JS execution longer than 50ms.
Fix:
- Break long tasks:
setTimeout,requestIdleCallback - Web Workers dla heavy compute
requestAnimationFramefor visual updates
3. Inefficient event handlers
// Złe — heavy work na każdy click
button.addEventListener('click', () => {
// 200ms of computation
for (let i = 0; i \< 10000; i++) {
expensiveOperation();
}
updateUI();
});
// Dobre — defer heavy work
button.addEventListener('click', () => {
// UI feedback immediate
showLoadingSpinner();
// Heavy work async
setTimeout(() => {
for (let i = 0; i \< 10000; i++) {
expensiveOperation();
}
hideLoadingSpinner();
updateUI();
}, 0);
});
4. Layout thrashing
Reading + writing DOM in same frame causes forced reflows.
// Złe — thrashing
elements.forEach(el => {
const height = el.offsetHeight; // read
el.style.height = height + 10 + 'px'; // write — forces reflow
});
// Dobre — batch reads, then writes
const heights = elements.map(el => el.offsetHeight); // all reads first
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px'; // all writes after
});
5. Third-party scripts
Chat widgets, analytics, ads. Often blocked main thread.
Fix:
- Defer loading (
<script defer>lubasync) - Self-host when possible
- Replace heavy chat with lightweight alternatives
- Audit usage — czy używasz wszystko?
6. React synchronous re-renders
Big React tree + frequent state changes = blocking renders.
Fix:
- React.memo for pure components
- useMemo / useCallback dla expensive calculations
- React Concurrent Mode / Suspense (React 18+)
- Virtualization dla long lists (react-window)
Jak debugować INP
Zanim cokolwiek naprawimy, chcemy zobaczyć konkretną interakcję, która psuje wynik — nie ogólny score, tylko klik, który trwa najdłużej.
Jest przy tym haczyk, o którym łatwo zapomnieć: INP powstaje na realnych użytkownikach, w labie go po prostu nie ma. Dlatego u siebie używamy TBT jako przybliżenia, z progiem w okolicach 200 ms, i liczymy medianę z trzech przebiegów zamiast ufać jednemu odczytowi. Na własnym artykule (12 lipca 2026, Lighthouse 13.0.1) trzy przebiegi dały TBT kolejno 908 ms, 31 ms i 25,5 ms — mediana 31 ms, czyli brak defektu długich zadań. Gdybyśmy zatrzymali się na pierwszym odczycie, mielibyśmy pozornie twarde uzasadnienie dla kilku godzin zupełnie niepotrzebnego cięcia JavaScriptu.
1. PageSpeed Wnioski
pagespeed.web.dev → mobile tab. Pokazuje:
- INP score
- Interactions causing high INP
- Recommendations
2. Chrome DevTools — Skuteczność tab
- Open DevTools (F12)
- Skuteczność tab
- Click record
- Interact z stroną (10-15 razy różne actions)
- Stop recording
- Look for Long Tasks (>50ms — orange/red)
- Drill into specific task — what's slow?
3. Web Vitals Chrome Extension
Real-time INP w Chrome toolbar. Mierzy actual user interactions na Twojej stronie.
4. Web Vitals JS library
<script type="module">
import {onINP} from 'https://unpkg.com/web-vitals?module';
onINP(metric => {
console.log('INP:', metric.value);
// Send to analytics
gtag('event', 'web_vitals', {
name: 'INP',
value: metric.value,
});
});
</script>
Real User Monitoring (RUM) — capture INP od actual users.
5. CrUX (Chrome User Experience Report)
https://pagespeed.web.dev/
Compare your INP vs competitors using real user data z Chrome.
Strategie optymalizacji INP
Kolejność, w jakiej podchodzimy do tych siedmiu strategii, zależy od tego, co pokazał debugging wyżej — nie warto zaczynać od web workerów, jeśli problemem jest jeden ciężki skrypt analityczny.
Strategy 1: Reduce JavaScript bundle
- Tree shaking — remove unused exports
- Code splitting — load chunks per route
- Bundle analysis (webpack-bundle-analyzer) — co jest big?
- Replace heavy libraries (moment.js → date-fns, lodash → native)
Strategy 2: Defer non-critical JS
<!-- Defer = wait for HTML parse, then execute in order -->
<script src="/main.js" defer></script>
<!-- Async = execute as soon as loaded -->
<script src="/analytics.js" async></script>
Strategy 3: Web Workers
Heavy compute → separate thread, doesn't block main thread:
// main.js
const worker = new Worker('/heavy-task.js');
worker.postMessage({data: largeArray});
worker.onmessage = (e) => {
console.log('Result:', e.data);
};
// heavy-task.js
self.onmessage = (e) => {
const result = expensiveProcessing(e.data.data);
self.postMessage(result);
};
Strategy 4: Optimize React
- React 18 + Concurrent rendering
- React.memo dla components
- useMemo, useCallback
- Virtualization dla list (>100 items)
- Suspense dla data fetching
Strategy 5: Reduce third-party impact
- Audit third-party scripts (Lighthouse → Third-party usage)
- Replace heavy with light (e.g. Crisp Chat → custom WhatsApp link)
- Self-host analytics (server-side tracking)
- Lazy load chat widgets (only after first scroll)
Strategy 6: Optimize event handlers
- Throttle / debounce frequent events (scroll, resize)
- Pasywne event listeners gdzie OK
- Use
onClickper element rather niż delegated parent
Strategy 7: Minimize DOM size
Large DOM (>1500 nodes) = slow layout calculations.
- Pagination (less items per page)
- Virtualization
- Reduce nesting levels
INP w różnych frameworks
Next.js / React
Common INP issues:
- Large bundle (default 200-500KB JS)
- Hydration cost
- Frequent re-renders
Fix:
- React Server Components (Next.js 13+)
- Dynamic imports
next/scriptz defer
Astro
Best dla INP — ships zero JS by default. Component „islands" hydrated only when needed. Jeśli klient pyta, jak w ogóle uniknąć problemu z INP zamiast go leczyć, to jest odpowiedź.
WordPress
INP often poor due to:
- Plugin JS bloat
- Theme JS
- Third-party (analytics, social, ads)
Fix:
- Aggressive lazy loading
- Caching plugin (WP Rocket)
- Plugin audit (remove unused)
- Lighter theme
Vanilla JS
Best control, lowest INP możliwe. Ale wymaga more dev time.
INP optimization checklist
- Bundle size <300KB JS (gzipped)
- No JavaScript blocking >50ms tasks
- Critical path JS deferred or async
- Third-party scripts lazy loaded
- Event handlers debounced/throttled gdzie applicable
- React components memoized
- Lists virtualized (>100 items)
- DOM <1500 nodes per page
- No layout thrashing
- Web Workers dla heavy compute
- Mobile-first testing (real devices)
- RUM monitoring deployed
Najczęstsze błędy w optymalizacji INP
- Testing tylko na desktop — mobile dwukrotnie wolniejsze
- Lab data only — Lighthouse nie capture real INP
- Synchronous heavy compute — blocks main thread
- Premature optimization — fix główne problemy first
- Removing analytics blindly — utrata data
- Brak monitoring — nie wiesz czy się poprawia
- Single optimization — cumulative impact = many small fixes
INP a Core Web Vitals overall
INP jest jednym z 3 CWV:
- LCP (Largest Contentful Paint) — loading speed
- CLS (Cumulative Layout Shift) — visual stability
- INP — responsiveness (zastąpiło FID)
Wszystkie muszą passing dla good Page Experience score.
Podsumowanie
INP 2026:
- Cel <200ms na 75th percentile mobile
- Główna przyczyna — heavy JavaScript blocking main thread
- Debug w DevTools — Skuteczność tab + Long tasks
- Real User Monitoring — Web Vitals library
- Optymalizacje: code splitting, defer, web workers
- Mobile-first testing — emulate slow CPU
- Continuous monitoring — INP changes z każdym deployment
Z tego, co widzimy w praktyce, INP to metryka, którą klienci najczęściej ignorują aż do momentu, gdy zobaczą, że strona konkurenta reaguje na kliknięcie natychmiast, a ich własna z opóźnieniem. To ranking factor, ale przede wszystkim user experience. Strona z INP 600ms frustruje users, którzy clickają multiple times — zwiększając INP further. Spirala. INP <200ms = smooth UX, retention, konwersje.
Audyt Core Web Vitals + INP — sprawdzimy obecną skuteczność i przygotujemy roadmap optymalizacji.
Chcesz osiągnąć podobne rezultaty? Zamów bezpłatną wycenę.