INP has become a critical Core Web Vitals metric — since March 2024 it replaces FID and accounts for all user interactions. Poor INP (over 200 ms) directly reduces conversion: each 100 ms delay in input or click drives away 1–2% of visitors. We help clients achieve ≤200 ms and confirm the result with instrumental monitoring through a combination of Long Tasks splitting, async filtering, and offloading heavy computations to Web Workers.
How INP Works
INP = time from user action (mousedown, keydown, pointerdown) to the next browser frame paint. The delay consists of three phases: input delay — waiting for the main thread to become free; processing time — executing event handlers; presentation delay — layout, paint, composite. A typical scenario: the user clicks a filter button, but the main thread is busy parsing an analytics script — the click waits 120 ms, then the handler runs for 80 ms, plus 50 ms for rendering — total 250 ms, already beyond acceptable.
Why INP Is Critical for SEO?
Google made INP one of three key ranking signals. Sites with INP > 200 ms lose rankings and get less organic traffic. For e-commerce, every millisecond of delay reduces conversion by 1–2%. In one project after reducing INP from 350 ms to 80 ms, conversion increased by 12%, and average depth per visit grew by 2 pages.
Diagnosing Slow Interactions
Use PerformanceObserver to collect all interactions with a delay >16 ms:
new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 200) { console.warn(`Slow interaction: ${entry.name}`, { duration: entry.duration, processingStart: entry.processingStart, processingEnd: entry.processingEnd, inputDelay: entry.processingStart - entry.startTime, processingTime: entry.processingEnd - entry.processingStart, presentationDelay: entry.startTime + entry.duration - entry.processingEnd, }); } } }).observe({ type: 'event', buffered: true, durationThreshold: 16 }); In Chrome DevTools → Performance → record the page → filter Long Tasks. Any task >50 ms is a candidate for optimization. In real sessions we often see Long Tasks from 100 to 500 ms, caused by third-party scripts or heavy main-thread computations.
Eliminating Long Tasks: Two Approaches
Splitting via yield suits lightweight computations. Web Worker is better for CPU-intensive tasks as it completely frees the main thread. A real case: in an online store, filtering 20,000 products in real time gave INP of 350 ms. We moved filtering to a Web Worker and added list virtualization — INP dropped to 80 ms.
// Async filter with yield every 50 elements async function filterProductsAsync(products, filters) { const results = []; for (let i = 0; i < products.length; i++) { if (matchesFilters(products[i], filters)) { results.push(products[i]); } if (i % 50 === 0 && i > 0) { await scheduler.yield(); // Chrome 115+ // fallback: await new Promise(r => setTimeout(r, 0)); } } return results; } // Web Worker – offload heavy filtering // worker.js self.onmessage = function({ data: { products, filters } }) { const results = products.filter(p => matchesFilters(p, filters)); self.postMessage(results); }; // main.js const worker = new Worker('/js/filter-worker.js'); worker.postMessage({ products, filters }); worker.onmessage = ({ data }) => setFilteredProducts(data); Optimizing React Components
Problem: synchronous filtering on every keystroke blocks the main thread. Solution — useTransition and virtualization. Here's a search component with instant feedback:
function ProductList() { const [query, setQuery] = useState(''); const [deferredQuery, setDeferredQuery] = useState(''); const filtered = useMemo( () => products.filter(p => p.name.toLowerCase().includes(deferredQuery.toLowerCase())), [deferredQuery] ); function handleChange(e: React.ChangeEvent<HTMLInputElement>) { const value = e.target.value; setQuery(value); // urgent – input responds immediately startTransition(() => { setDeferredQuery(value); // non-critical – list updates later }); } return ( <> <input value={query} onChange={handleChange} /> <ul> {filtered.map(p => <ProductItem key={p.id} product={p} />)} </ul> </> ); } For long lists, use virtualization:
import { useVirtualizer } from '@tanstack/react-virtual'; function VirtualProductList({ products }: { products: Product[] }) { const parentRef = useRef<HTMLDivElement>(null); const rowVirtualizer = useVirtualizer({ count: products.length, getScrollElement: () => parentRef.current, estimateSize: () => 80, overscan: 5, }); return ( <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}> <div style={{ height: rowVirtualizer.getTotalSize() }}> {rowVirtualizer.getVirtualItems().map(virtualRow => ( <div key={virtualRow.index} style={{ transform: `translateY(${virtualRow.start}px)`, position: 'absolute', width: '100%' }}> <ProductItem product={products[virtualRow.index]} /> </div> ))} </div> </div> ); } Third-party Scripts: Hidden Threat
Chats, pixels, analytics — common causes of poor INP. They run on the main thread and block interactions. Solutions:
- Load after main content (setTimeout 3s after load).
- Use Partytown to run scripts in a Web Worker.
Typical INP Optimization Mistakes
- Optimizing only one interaction, while INP takes the worst.
- Ignoring presentation delay — sometimes paint takes longer than the handler.
- Using
setTimeout(0)instead ofscheduler.yield()— the former doesn't guarantee yielding the thread. - Forgetting mobiles: on weak CPUs Long Tasks occur more often.
How to Measure INP in Real Time?
Set up Real User Monitoring (RUM) sending metrics to analytics. Filter bots using navigator.webdriver. Example: collect all interactions with INP >200 ms into performanceEntries and send to backend for analysis. This reveals problematic pages and devices.
INP Target Values
| Interaction type | Target |
|---|---|
| Button click | < 100 ms |
| Input in search field | < 150 ms |
| Opening modal | < 200 ms |
| Catalog filtering | < 200 ms |
INP Optimization Process
- Diagnostics — collect real metrics via RUM and lab tests.
- Analysis — identify top 5 problematic interactions with exact delay values.
- Implementation — apply techniques (yield, Workers, lazy loading, virtualization).
- Testing — A/B experiments, checking impact on conversion and SEO traffic.
- Monitoring — set up continuous INP tracking with alerts when exceeding 200 ms.
Estimated Timeline
From 3 to 7 days — depending on number of problematic interactions and architectural complexity. Pricing is determined individually after an audit.
Over 50 successful INP optimizations. We guarantee achieving ≤200 ms or your money back. Contact us for an audit and receive an optimization plan within 3 days. Request a consultation — we'll review your case for free.







