If you have noticed your Lighthouse and Google Search Console performance scores dropping recently due to a metric you've never heard of, you aren't alone. Interaction to Next Paint (INP) has officially replaced First Input Delay (FID) as the defining Core Web Vital for responsiveness.
While FID only measured the delay of the very first click, INP is far more merciless: it measures the responsiveness of every single tap, click, and keyboard interaction throughout the entire lifecycle of a user's visit.
Here is everything you need to know to diagnose and fix bad INP scores, speed up your UI, and keep the Google algorithm happy.
What Exactly is INP?
Interaction to Next Paint tracks the latency of all interactions a user makes with a page, and reports a single value which all but the worst interactions were below. A low INP means the page was consistently able to respond quickly to the vast majority of user interactions.
The lifecycle of an interaction has three phases:
- Input Delay: The time from when the user clicks/taps to when the event handlers start running. This is usually caused by the main thread being blocked by other tasks.
- Processing Time: The time the browser spends actually executing the code inside your event handlers.
- Presentation Delay: The time it takes the browser to calculate the new layout, paint the pixels, and present the new frame to the screen.
To have a "Good" INP score, your total interaction latency must be under 200 milliseconds. Above 500 milliseconds is considered "Poor".
Why INP Matters for SEO and UX
Google uses Core Web Vitals as a ranking factor. A poor INP score can directly impact your search engine visibility. But perhaps more importantly, high INP frustrates users. If a user clicks an "Add to Cart" button or opens a mobile menu and nothing happens for half a second, they assume the page is broken, leading to rage-clicks and higher bounce rates.
How to Optimize INP (Actionable Strategies)
Fixing INP generally comes down to one golden rule: Unblock the Main Thread. The browser's main thread handles JavaScript execution, garbage collection, layout, and painting. If it's busy parsing a massive 2MB JavaScript bundle, it cannot respond to user clicks.
1. Break Up Long Tasks (Yield to the Main Thread)
The most common cause of high Input Delay is "Long Tasks" (any JavaScript task taking longer than 50ms). When the main thread is occupied by a long task, clicks have to wait in a queue.
The Fix: Break up heavy work by explicitly yielding to the main thread. This allows the browser to pause your heavy script, handle any pending user interactions, and then resume the script.
// The Old Way: Blocks the main thread
function processMassiveData(data) {
for (let i = 0; i < data.length; i++) {
expensiveCalculation(data[i]);
}
}
// The Modern Way: Yielding using scheduler.yield() or setTimeout
async function processMassiveDataYielding(data) {
for (let i = 0; i < data.length; i++) {
expensiveCalculation(data[i]);
// Every 50 items, yield back to the main thread
if (i % 50 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}
2. Acknowledge the Input Immediately
When a user clicks, provide instant visual feedback before starting heavy processing. For example, if saving a form requires complex validation and an API call, turn the button into a loading spinner instantly.
saveButton.addEventListener('click', async () => {
// 1. Instant feedback (Lowers INP)
saveButton.classList.add('loading');
saveButton.disabled = true;
// 2. Yield so the browser can paint the loading spinner!
await new Promise(resolve => setTimeout(resolve, 0));
// 3. Now run the heavy processing
await performHeavyValidation();
await submitToServer();
});
3. Avoid Layout Thrashing
Presentation delay happens when your JavaScript forces the browser to recalculate the layout of the page synchronously. This usually occurs when you write to the DOM, then immediately read from it (e.g., getting an element's width), forcing a "reflow".
Instead, batch your DOM reads together, and batch your DOM writes together. You can use requestAnimationFrame to synchronize your DOM updates with the browser's rendering pipeline.
4. Remove Non-Essential Third-Party Scripts
Third-party scripts (analytics, chatbots, ad networks) are notorious for monopolizing the main thread.
- Audit them: Do you really need 4 different analytics tracking scripts?
- Delay them: Load non-essential scripts using the
deferattribute, or delay their execution until after the page has become idle using requestIdleCallback or libraries likepartytownto move them to a web worker.
Conclusion
Optimizing INP isn't just about satisfying a Lighthouse metric; it's about building websites that feel snappy, responsive, and robust. By keeping your event handlers incredibly lightweight, yielding during heavy processing, and removing main-thread bloat, you'll provide a better user experience and reap the SEO rewards.