Interaction to Next Paint: how to find and fix a slow INP
INP measures how quickly a page responds when someone clicks, taps or presses a key. Aim for 200 ms or less. Slow INP usually comes from JavaScript keeping the browser busy, so find the slow interaction, see which phase takes longest, then break up that work.
What INP measures
Interaction to Next Paint is the Core Web Vital for responsiveness. It times how long the page takes to show a visual response after an interaction. It looks at the whole visit, not just the first click.
| Good | Needs improvement | Poor |
|---|---|---|
| 200 ms or less | Over 200 ms, up to 500 ms | Over 500 ms |
- What counts: mouse clicks, taps and key presses.
- What doesn't: hovering, scrolling and zooming on their own.
- Which interaction sets the score: usually the slowest one. On pages with many interactions, one of the slowest is ignored for every 50.
Why tests can miss it
A page has no INP until someone interacts with it. A bot or crawler that doesn't click gets no value, and some lab tools don't report it. Total Blocking Time can be a rough stand-in, but it doesn't measure a real interaction.
The score Google uses comes from real Chrome visitors at the 75th percentile, split by mobile and desktop. See it in Search Console's Core Web Vitals report or PageSpeed Insights, where a page has enough visits. Then reproduce it yourself, ideally on a mid-range phone or with CPU throttling, as a fast laptop can hide the problem.
How to find the slow interaction
1Run the snippet 3 minutes
Load the page, open DevTools (F12), go to the Console and paste this. Then use the page: open menus, add to basket, type in search, apply filters.
// Logs each click, tap or key press event and where its time went
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.interactionId) continue;
const inputDelay = Math.round(entry.processingStart - entry.startTime);
const processing = Math.round(entry.processingEnd - entry.processingStart);
const presentation = Math.max(0, Math.round(entry.startTime + entry.duration - entry.processingEnd));
console.log(entry.name, entry.duration + ' ms', { inputDelay, processing, presentation }, entry.target);
}
}).observe({ type: 'event', durationThreshold: 16, buffered: true });
One interaction can log several events with the same duration, such as pointerup and click. Look for the longest duration, then find the event with the high processing time, as that's the one whose code ran slowly.
2Use the Performance panel 5 minutes
Record in the Performance tab while you repeat that interaction. It shows which scripts ran and for how long, so you can see who owns the slow work.
Try interactions during load as well as after it. A click while scripts are still starting up is often the slowest one.
Where the time goes
| Phase | What it is | Usual cause |
|---|---|---|
| Input delay | Waiting for the browser to start your event code | Long tasks already running, often during load |
| Processing | Running your event handlers | Too much work in the handler |
| Presentation delay | Getting from the finished handler to the next frame | A large page or heavy rendering |
How to fix it
- Input delay: break up long tasks during load, and find out whether a third-party script is the one holding the browser up.
- Processing: show the visual change first, then defer everything else, such as saving or analytics. Keep handlers small.
- Layout work: don't read layout values straight after changing styles in the same task, as that forces a layout.
- Presentation delay: reduce DOM size, add elements when they're needed, and consider
content-visibilityfor off-screen sections. Be careful rendering large chunks of HTML from JavaScript.
Handing control back to the browser lets it paint before the heavy work starts:
button.addEventListener('click', async () => {
showLoadingState(); // Visible change first
await new Promise((resolve) => setTimeout(resolve)); // Hand back to the browser
runSlowWork(); // Then the heavy work
});
A loading state isn't a fix for slow work, but it does mean the visitor sees a response straight away. For the other two Core Web Vitals, see Largest Contentful Paint and Cumulative Layout Shift.
How crawlers report this
Crawlers report likely causes, not which click was slow. These labels come from PageSpeed data, so they can change between versions.
| Screaming Frog | Sitebulb | What it means |
|---|---|---|
| PageSpeed: Reduce JavaScript Execution Time | No direct match | Scripts take a long time to run. |
| PageSpeed: Minimize Main-Thread Work | No direct match | The browser is busy, so it responds later. |
| PageSpeed: Reduce Unused JavaScript | Remove unused JavaScript | The page loads code it doesn't use. |
| PageSpeed: Optimize DOM Size | Avoid excessive DOM size | A large page makes each update slower. |
Handing this to a developer?
See the slowest interaction
SEOSnip's Speed tab measures INP as you use the page. Click a button or menu, then reopen the extension. It's your browser's result, not field data, so use it to find the cause. You get:
- INP with a good, needs improvement or poor rating
- The slowest interaction and the element involved
- Its input delay, processing and paint time
- How many interactions were measured
- A ticket with those numbers attached