Od 12. března 2024, kdy INP nahradil First Input Delay v sadě Core Web Vitals, uplynuly dva roky. Threshold zůstává: dobré skóre je 200 ms na 75. percentilu, mezi 200 a 500 ms je „needs improvement“, nad 500 ms poor. Co se ale změnilo, je tooling. Chrome 129 v září 2024 oficiálně dostal scheduler.yield() a v 2026 už je dostupný i ve stable Edge. Pro většinu pomalých interakcí v JS-heavy aplikacích je to nejjednodušší fix, který reálně hne čísly.
Co INP měří a proč padá
INP měří latenci od user inputu (klik, klávesa, tap) k vykreslení dalšího framu, který odráží odpověď. Bere nejhorší interakci na stránce za celou návštěvu, ne průměr. Důvod, proč padá u většiny CMS a e-shopových šablon:
- ▸Long task — jakákoliv JS úloha nad 50 ms blokuje hlavní vlákno a posouvá next paint.
- ▸Hydration React/Next.js komponent na velkých stránkách produkuje 300–800 ms tasky při interakci s nehydrated subtree.
- ▸Třetí strany — analytics, A/B test SDK, chatboty — běhají na main threadu a queueují své vlastní tasky před vaše.
- ▸DOM s 4000+ uzly způsobuje pomalý style/layout recalc i u triviálních změn.
„Long tasks delay the browser's ability to respond to user input, creating a perception that a site is slow. With scheduler.yield(), you can break up long tasks into smaller chunks, improving responsiveness by explicitly yielding back to the main thread.“
scheduler.yield() prakticky
Klasický pattern setTimeout(fn, 0) pro dělení tasku má jednu vadu: continuace skončí na konci queue, takže vás tam předběhnou taski třetích stran. scheduler.yield() vrací promise, jejíž continuace má prioritu — pokračujete dřív, než spustí cokoliv jiného.
- ▸async function processItems(items) { for (const item of items) { doWork(item); if (needsYield()) await scheduler.yield(); } }
- ▸Polyfill: const yieldToMain = () => 'scheduler' in window && 'yield' in scheduler ? scheduler.yield() : new Promise(r => setTimeout(r, 0));
- ▸needsYield() typicky: performance.now() - taskStart > 50 nebo navigator.scheduling.isInputPending(). isInputPending vrací true, pokud uživatel klikl/píše a my drž blokujeme.
Kde to měřit
Lighthouse v lab prostředí INP nezachytí spolehlivě — je založen na simulovaných interakcích. Pravdivá data jsou v Chrome User Experience Report (CrUX) a v real-user monitoringu. Knihovna web-vitals na NPM přímo emituje INP eventy. Pro lokální debug Performance panel v DevTools má od Chrome 130 sekci Interactions, kde vidíte breakdown na input delay, processing time a presentation delay.
Praktická hranice: pokud v Performance panelu vidíte processing time přes 100 ms, to je váš problém. Input delay nad 50 ms znamená, že nějaký jiný task běžel, když uživatel klikl — typicky analytika nebo hydration.
Co dělat kromě yieldu
- ▸Defer hydration u Next.js: použít React Server Components a hydrovat jen interaktivní islandy.
- ▸Třetí strany izolovat do Web Workeru přes Partytown, kde to dává smysl (analytika typicky ano, A/B test ne).
- ▸Virtualizovat dlouhé listy — tanstack-virtual sníží DOM uzly z 5000 na 50 a INP klesne i bez dalších úprav.
- ▸content-visibility: auto na off-screen sekce, aby browser neflikoval style recalc.
- ▸Optimistic UI updaty — vykreslit intermediate stav okamžitě a server response zpracovat asynchronně.
Co s tím
Pro nový projekt: měřit INP v real-user monitoringu od dne jedna, ne čekat na to, až Search Console začne zobrazovat červená čísla. U existujícího webu s INP nad 200 ms začít DevTools Performance Interactions panelem a najít tři nejhorší interakce. V devíti z deseti případů je řešením kombinace scheduler.yield() v custom kódu, deferred hydration a izolace třetích stran — ne refaktor frameworku. INP není o tom, jak rychle váš JS běží. Je o tom, jak často pustí uživatele ke slovu.