Interaction to Next Paint measures how responsive a page is to visitor interactions. Learn how to identify and debug slow interactions – and most important, how to make them faster!
Interaction to Next Paint measures how responsive a page is to visitor interactions. It measures the elapsed time between a tap, a click, or a keypress and the browser next painting to the screen.
INP breaks down into three sub-parts
Pages can have multiple interactions, so the INP time you'll see reported by RUM products and other tools, such as Google Search Console and Chrome's UX Report (CrUX), will generally be the worst/highest INP time at the 75th percentile.
Like all Core Web Vitals, INP has a set of thresholds:
INP thresholds for Good, Needs Improvement, and Poor
Many sites tend to be in the Needs Improvement or Poor categories. While getting to Good is achievable, it's not always easy.
This post covers
The Chrome UX Report (CrUX) can provide a high-level view of INP. Individual pages can be spot checked via the CrUX API or tools such as Page Speed Insights
But as you can see in how to find (and fix!) INP interactions on your pages, CrUX is no substitute for having your own RUM data that you can group and filter by dimensions such as the different page and device types.
Start with the Web Vitals heatmap on SpeedCurve's RUM > Performance dashboard. It gives a high-level summary that can be filtered by page label to check if the behavior is consistent across all paths in the group.
Heatmap of the most popular pages and their Web Vitals metrics
Then switch to the RUM > Design dashboard and use the list of popular interaction elements to determine which ones to investigate further.
Most popular interactions
Knowing which page types have high INP times and the interactions visitors are using on those pages is really effective at identifying interactions to investigate further.
If you don't have RUM data, think about the common interactions visitors are likely to use – dismissing cookie dialogs, opening menus, zooming on product images, etc. – and investigate those further. The caveat here is that it's not as effective as having RUM data to work from and can lead to improvements that don't seem to influence INP much.
Once you know which pages have high INP times, and what are the popular interactions on those pages, switch to Chrome DevTools, profile the interactions, and identify ways to improve them.
The Performance panel can be overwhelming – even for experienced engineers – as it exposes how much work the browser is doing to load pages or handle interactions.
Here is one approach to use when debugging interactions. Although Chrome Canary is used in these examples, the same approach works in stable Chrome and other Chromium-based browsers.
As guest user profiles don't load extensions, they help minimise some of the noise that extensions and other factors can have on performance analysis.
Guest profiles also start with empty caches, empty cookie stores, empty browser storage, etc. These may get populated during testing, but we can clear them at any time via Application > Storage > Clear Site Data in DevTools.
Opening a guest profile in Chrome
As mobile visitors tend to be the majority of visitors for most sites, switch to mobile emulation.
Switching to the DevTools Performance panel
Load the page you want to investigate. Wait until has finished load before profiling it.
After the page has loaded, press the record icon in the DevTools toolbar, wait for the profile to start recording, and then interact with the page.
The profiler starting up often creates a Long Task right at the start of the profile, so wait a second or so before actually interacting.
Recording a profile
After you've recorded data on the interactions you're interested in, stop recording. After a few moments you should be greeted with a view something like the one below.
In this view, you can see the opened tracks for Frames, Interactions, and Main Thread, so you can see what's visible on the page when it was interacted with, as well as the activity that happened on the main thread.
Example performance profile in Chrome DevTools
In the Main Thread track you can see the Profiling Overhead task right at the start of the profile and then a second call stack in response to the interaction.
A quick guide to interpreting this panel:
Clicking on an individual cell will show more detail in the summary panel at the bottom of the tab (not shown in screenshot). You can zoom in/out and scroll using either the mouse or the W A S D keys.
After we've captured profiles, we can start analyzing them to understand why we're seeing long INP times, and perhaps more importantly, what we can to do reduce them.
Here are three examples that illustrate the common issues.
The examples were captured in Chrome Canary on a 2020 i5 MacBook Pro without CPU throttling enabled. If CPU throttling was enabled or the tests were carried out on an actual Android device, then I'd expect the INP times to be higher.
If you want to explore the traces in more detail, they're uploaded to Paul Irish's trace.cafe
The menu on the mobile version of H&M was opened by clicking on the icon in the top right.
Even though the page was only clicked once, multiple event handlers were invoked. The one for the menu was the longest and had INP time of 350ms – in other words, 150ms longer than Google's 200ms threshold for 'Good'.
Long interaction when opening the menu on H&M
In this case, most of the time is spent in the actual event handler (Processing Time) for the menu, but there is a slight delay before the event handler can execute.
Examining the flame chart reveals four main groups of processing that happen in response to the interaction:
Main thread activity when opening the menu on H&M
Start by focusing on what's the source of the long Processing Time, asking questions such as:
The trace for John Lewis shows similar patterns to the one for H&M.
Again there's another event handler that fires before the handler for the menu, but the execution of the both handlers is also delayed by a separate task on the Main Thread. These tasks create a 170ms Input Delay before the interaction handler for the menu executes.
Long interaction when opening the menu on John Lewis
Breaking down the main thread activity shows eight groups of activities that delay the response to the interaction:
John Lewis uses New Relic. New Relic wraps many of the script calls, and this has some impact on the duration of the tasks. To investigate further, you could profile with New Relic disabled to measure what impact it's having (if any).
Main thread activity when opening the menu on John Lewis
The main question to ask when seeing this kind of profile:
What can be done to reduce the Input Delay – the Long Task at the start, then focusing in on the intermediate event handlers, and lastly the style and layout calculations?
(In the chart above, the whisker for the Presentation Delay extends into a GTM task, but this is most likely a Chrome issue. You might also notice Chrome Canary doubles up some Long Tasks in the Profile, too.)
> Explore the trace
For the last example, we've closed the consent dialog that all sites in Europe are required to display before they inject third-parties such as ads and analytics into the page.
Here the main issue is the amount of work the event handler is trying to complete in a single task. The Processing Time for the interaction is 382ms
Long interaction when clicking 'accept' on Wales Online
Examining the flame chart reveals six main groups of processing that happen in response to the interaction:
Main thread activity after clicking 'accept' on Wales Online
One thing that's noticeable with the Wales Online example is that the Processing Time is entirely due to third-party scripts. That can limit the options to reduce it, but even then it should be possible to divide the task up.
> Explore the trace
After we've identified why an interaction has a high INP time, our next goal is to reduce it. Separating how I think about Input Delay versus Processing Time and Presentation Delay can help.
Split between Input Delay, and combined Processing Time and Presentation Delay
As Processing Time and Presentation Delay are easier to identify and fix, let's cover them first before moving on to Input Delay.
When it comes to reducing Processing Time and Presentation Delay, many articles focus on breaking up Long Tasks up or 'yielding to the main thread' with setTimeout
, scheduler.yield
or requestIdleCallback
, etc.
While that is one place to start, it's not the only approach. Reducing the time Long Tasks take to execute is just as important as splitting tasks up.
Another guiding view is to focus on what's most important from a user perspective and optimize that. For example, if the user is opening a menu, then showing them the menu is the most important activity. Anything else that might be triggered by the same action should be secondary.
Both the H&M and John Lewis menus record an analytics event when someone opens the menu. These calls happen before the menu is actually displayed. (This pattern occurs on many other sites, too.)
While analytic events are useful to help us understand our visitors' behavior, they're secondary and shouldn't delay the visitors primary goal, in this case opening the menu.
Scheduling these events into a new task via setTimeout (e.g. setTimeout(analytics_fn, 0)
or scheduler.postTask
for browsers that support it) moves the work out of the interaction handler to be executed later and allows the interaction handler to complete sooner.
The same method can be used with Wales Online's consent manager. After the visitor has clicked 'accept' or 'reject' they want to get on and read the news rather than wait for multiple ad providers to be given permission to load (or not). Scheduling setConsentInfo
into a separate task enables the browser to paint the next frame sooner, while the ad providers can carry on loading in the background.
Here's an example of what this looks like in a trace:
Breaking up a click event handler by scheduling work into a separate task
The Long Task was originally part of the click event handler. Using setTimeout
to schedule it into its own separate task allows the browser to paint before the Long Task executes. The Long Task might still be a problematic if someone interacts while it's executing, but it's no longer part of the click handler's INP.
We've seen many examples where publishers have improved INP by scheduling the setting of consent into a separate task. We expect consent managers to adopt this approach as a default.
Ryan Townsend spoke about The Unbearable Weight of Massive JavaScript at performance.now() in November 2023. He shared a case study where they replaced over 50,000 lines of JavaScript with native HTML and CSS features. The result was a faster user experience with more maintainable codebase.
H&M relies on JavaScript components to create the menu elements, add them to the DOM, and apply styles. The result: a processing time of 303ms.
Long interaction when opening the menu on H&M
Let's compare this to another fashion retailer, French Connection. French Connection creates the menu elements when they render the page server-side, and then just changes the elements styles to display the menu. This illustrates the dramatic difference in processing time between the two approaches:
Interaction when opening the menu on French Connection
Of course the French Connection page is going to have more DOM elements. When tools like Lighthouse warn you to "avoid an excessive DOM size", it's tempting to choose other approaches without perhaps fully considering the tradeoffs. Menus often contain large numbers of DOM elements. The choice is whether they're created when the page is initially generated, or at runtime using JavaScript when the menu is requested.
Lighthouse warns about DOM size because it "will increase memory usage, cause longer style calculations, and produce costly layout reflows." But when used carefully, CSS properties like content-visibility, isolation, and will-change can help reduce the cost of a large number of DOM nodes.
Talking of CSS, you might have noticed that in some of the examples above, INP was affected by some long style and layout calculations. These were caused by interaction handlers injecting styles that affected the whole DOM, or by interaction handlers querying style or size properties via methods like getComputedStyle or getBoundingClientRect, so forcing recalculations. (Paul Irish keeps a list of JavaScript methods that typically trigger style and layout calculations.)
Think about the work you're asking the browser to do and whether there are more efficient ways of achieving the end result.
Sometimes there isn't work that can be deferred, or the efficiency of interaction handlers can't be improved. In those cases, we just need give control back to the main thread so that it can get on with painting the next frame.
One approach is to use setTimeout wrapped in a Promise:
function yieldToMain() {
return new Promise(resolve => {
setTimeout(resolve,0);
});
}
And then at suitable points in the code insert:
// Yield to the main thread:
await yieldToMain();
setTimeout creates a new task, so enabling the browser's scheduler to take over and process other tasks like input before resuming.
Jeremy Wagner discusses this approach in more detail in his web.dev posts on Optimizing INP and Optimizing Long Tasks.
The other option to consider for tasks that are hard to optimize is whether the work they're doing can be moved off the main thread via a Web Worker.
Diagnosing the causes of slow Input Delay isn't always easy with just Synthetic monitoring and DevTools. That's because the length of the delay depends on when the visitor interacted with the page and what tasks were executing when the visitor interacted.
The Long Animations Frame (LoAF) API, which is set to ship in Chrome 123, will help RUM tools identify the tasks that caused Input Delay. Once identified, we will be able to profile the tasks in DevTools.
Until LoAF is widely supported, there are profiling approaches in DevTools that can help identify some of the problematic scripts.
As the H&M and John Lewis examples demonstrated, other touch, mouse, and keyboard event handlers are also triggered by interactions and can execute before our event handler for the main interaction.
Fortunately these event handlers are captured in the DevTools profile. We can also inspect which event handlers are active using the Event Listeners panel in the right sidebar of the Elements panel in DevTools.
Viewing active event listeners in Chrome DevTools
Some monitoring products, such as New Relic, wrap other scripts calls. This can make identifying the code that's actually going to execute a bit harder. To identify the actual event handler, you can either:
Once we've identified event listeners that are active for click, key, mouse, and tap events, we can review and remove any that aren't really necessary.
If third-party tags are adding their own event handlers, then it's a case of:
Some third-party providers are serious about addressing the impact they have on INP. If you're using one that's not, then I'd advocate switching to an alternative.
It's harder to identify any other Long Tasks that contribute to Input Delay. It depends on what task is executing when the visitor interacts (this is where LoAF will really help) and how long the task continued executing after the initial interaction.
But we do know there is a relationship between total Long Task Time and INP:
Relationship between Long Tasks and Interaction to Next Paint (INP)
And we also know that visitors start to interact shortly after they start to see content:
Relationship between First Contentful Paint (FCP) and First Click Interaction
The exact relationships will vary from site to site, but the overall pattern was pretty consistent across the many sites I checked.
Knowing this, we can make an informed guess that any Long Tasks that occur after useful content starts to appear are in danger of contributing to Input Delay.
In John Lewis' case, profiling the page while it loads shows there are a bunch of Long Tasks that happen after 2.2s – notice the gap in the filmstrip – and these are likely to lead to higher INP times if the visitor tries to interact at this point.
Profile showing Long Tasks during John Lewis home page loading
Some other things you might want to experiment with:
As far as optimising these Long Tasks goes, my advice is very similar to optimising slow interaction handlers:
One thing to watch out for:
There's often a Long Task just before the DOM Content Loaded event. This is because any deferred and module scripts execute as a single – potentially long – task just before DOM Content Loaded. Until browser makers change this behaviour, there's always a potential for this Long Task to create Input Delay if someone interacts at this point.
Getting to the root of high INP times and fixing them can be quite complex and sometimes overwhelming. It's important to remember that even small incremental changes add up to larger overall improvements.
Some other things to keep in mind...
RUM is great for quickly identifying pages and interactions with high INP times. But even without RUM it's possible to start improving INP. There's just a danger that you might not be profiling the most influential interactions.
When it comes to actually understanding and optimizing INP:
This is especially true if a site is heavily reliant on a JavaScript framework.
You may come across things that don't quite make sense. I've seen requestAnimationFrame
and setTimeout
loops delay input handlers. I've also seen JavaScript dialogs really affect INP. As with all Core Web Vitals, it's a work in progress. I expect the Chrome team will address some of these edge cases eventually.
The reported INP measurement represents the worst interaction on the page ,and pages can have many interactions. If the slowest interaction has an INP time of 500ms, and the second slowest has an INP time of 450ms, then fixing the worst interaction will only reduce INP by 50ms!
To borrow the words of Pokémon, you gotta catch 'em all!
I've been using the techniques from this post to help companies improve their INP time. For one company, we reduced INP by more than 50% in the space of just a couple of weeks.
Reduction in INP due to Long Tasks improvements
If you'd like to start measuring INP effectively, we offer a free 30-day trial that includes both real user and synthetic monitoring.
We also have some of the most experienced web performance consultants in the world. If you're not sure where to start with INP, or are stuck with what to do next, feel free to get in touch via support@speedcurve.com.