Interaction to Next Paint - Processing Time

Learn how to find and improve INP issues caused by processing time

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

Interaction to Next Paint (INP) issues cause by processing time

In our previous article we talked about the interaction to next paint on how to identify interaction to next paint issues. If you would like to read up on the basics this is a great place to start!

In this article I will focus on 'processing time'. How this affects the Interaction To Next Paint issues and then explain how to minimize  processing time to improve the interaction to next paint!

In short: The Interaction to Next Paint (INP) measures how long it takes for a to see a visual change on a page after a user has interacted with the page. This INP can be broken down into 3 components: 'input delay', 'processing time' and 'presentation delay'. Processing time is a major contributor to the total INP, accounting for roughly 40% of the delay on average.  This means that optimizing your JavaScript code, particularly event handlers, can significantly impact your website's INP score.


INP TIP: processing time can be optimized by immediately running important code that precedes the layout update and scheduling all other code to run after that.


What is processing time?

inp 3 stage processing time highlighted

The Interaction to Newt Paint (INP) can be broken down into 3 sub-part: 'Input Delay', 'Processing Time' and 'Presentation Delay'

Processing time refers to the time it takes for the browser to process the associated event callback after a user interacts with a web page (e.g., clicking a button or pressing a key). While there is always some processing time INP issues occur when the event callbacks take up too much processing time.

Processing time and the INP

Processing Time might be 'the thing' when you think about optimizing the Interaction To Next Paint. It is the 'job that needs to be done' before the layout can be updated by the browser.

Many developers think about improving the INP in terms of optimizing callback function (optimizing the process time) and they are right. But in terms of importance it processing time is not even the most important part to improve but it still, on average makes up about 40% of the total INP time.

inp distribution input delay highlighted

At CoreDash we collect millions of core web vitals data point each hour. Based on that data the Processing time accounts for 40% of the Interaction to Next Paint. And while that is a lot optimizing the processing time alone will most likely not be enough to fix INP issues

Processing time example: When a user interacts with a web page, such as clicking a button, the time it takes for the event handler associated with that button click to complete its execution is known as processing time. For instance, if a user clicks a button to submit a form, the code that validates the form data, sends it to the server, and handles the response would contribute to the processing time. The longer these operations take, the longer the processing time, and potentially the worse the INP score.

What causes high processing time?

To start fixing INP issues cause by high processing time we need to understand what the possible cause of high processing time can be.  High processing time, the time it takes for event callbacks to run to completion, can be caused by unneeded code, unoptimized code, clustered callbacks, and layout trashing. Let's take a better look at those 4 areas.

inp processing time clustered
  1. Unneeded code. Old, unused code or code without immediate relevance to the user interaction, can prolong callback execution time.
  2. Un-optimized code. Inefficient code (usually usually loops or inefficient DOM lookups) can make the event callbacks rum slower then it needs to.
  3. Clustered callbacksMultiple event callbacks scheduled close together create a queue. If a callback triggered by the user interaction gets stuck in this queue, the response appears delayed.
  4. Layout trashing: Frequent DOM manipulations that trigger layout recalculations can strain the browser and lead to performance regressions.

Minimize processing time

To improve the INP cause by long processing time we obviously need to improve processing time. But how do we do that? 

inp processing time optimized

To minimize processing time we need to make the code, that is responsible for the subsequent layout update, runs as fast as possible. We can do this by optimizing existing code (remove unneeded code and optimize the current code) and by distinguish between code that needs to run before and after the layout update. Basically code that is critical to the layout update needs to run before it and all other code can run after the layout update.

  1. Remove unused code.  While removing unused might seem like a no-brainer, on most sites there is at least some old-unused code that just executes without really adding anything to the page nor the UX.  This means the first thing to do is to ensure there is no code running that is not needed. This is can be done in many ways. For example by  a process called tree shaking or code splitting. Or manually by inspecting your code coverage in chrome and also by using a good IDE that will hint at unused code. (Pro tip: also take a critical look at resources loaded by your Tag Manager)
  2. Minimize Callback Execution Time: Use a JavaScript profiler to identify bottlenecks in your code and target those areas for optimization. Consider techniques like memorization, pre-calculation, and caching to avoid redundant computations. (Tip: you can use the chrome performance panel to find scripts with long execution time!)
  3. Prioritize critical code and schedule other code: When the callback code has been optimized split the code into code that needs to run immediately and code that can be deferred. Take a look at this real-life example:
    inp processing time clustered callbacks react
    In this example Google Tag Manager and Facebook events callback are executed before the (REACT) code that preceded the layout update. The solution would have been to schedule the GTM callbacks when the browser is idle
  4. Avoid Layout Trashing or reflow. Layout thrashing happens when style updates and style reads reads are mixed in a loop, causing the browser to recalculate the layout numerous times. 
    To avoid layout thrashing, perform all style changes (the "sets") before requesting style values (the "gets"). This approach minimizes the frequency of layout updates, leading to a faster webpage.
    For instance, in a loop that sets each paragraph's width to match an element's width, read the element's width once before the loop and use that value to update the paragraphs' widths inside the loop.

How to Prioritize Critical Code

The last item 'Prioritize critical code and schedule other code' might be a bit abstract for many of you. We can prioritize Critical Code by using requestIdleCallback() and by yielding to the main thread.

We use requestIdleCallback for less important tasks that do not need to run immediately: Here is a before and after example of scheduling a GTM event.

/* before :: immediately run code  */
gtag('event', '<event_name>', {
   'event_category': '<event_category>',
 });

/* after :: run the same code during browser idle */
requestIdleCallback(() => {
  gtag('event', '<event_name>', {
    'event_category': '<event_category>',
  });
}, { timeout: 1000 });

The downside of requestIdleCallback is that code might not run as soon as you would like. In that case we can 'yield to the main thread' after the most important code has run and in doing so give the browser a moment to run the layout updates. Here is an example on how to break up tasks by yielding to the main thread

async function yieldToMain() {
  if ('scheduler' in window && 'yield' in window.scheduler) {
    return await window.scheduler.yield();
  }
  return new Promise((resolve) => {
    setTimeout(resolve, 0);
  });
}

async function handleClick(){
 // do the most important layout updates here
 await yieldToMain();
 // do other tasks that need to run as asap after the layout update
}

In te future we could do much more with the window.scheduler then just yield to the main thread. . We could also Prioritize tasks using the scheduler API (see Support Table).

The scheduler API provides the postTask() function for finer-grained scheduling of tasks by setting priorities, which helps the browser prioritize work so that low-priority tasks yield to the main thread. 

The postTask() function accepts three priority settings: 'background' for the lowest priority tasks, 'user-visible' for medium-priority tasks, and 'user-blocking' for critical tasks requiring high priority. 

By prioritizing critical tasks with 'user-blocking', developers can ensure they are executed with higher priority, allowing the browser to handle user interactions more responsively. The scheduler API provides the postTask() function for finer-grained scheduling of tasks by setting priorities, which helps the browser prioritize work so that low-priority tasks yield to the main thread. 

Practical Implications

Let's get to the most important question: 'What does this all mean for my site'. Let's break it down for WordPress & REACT and see how you can improve the Interaction to Next Paint when it comes to processing time.

WordPress

WordPress offers very little control when it comes to scripts. Many scripts are added through plugins. Most of the times those scripts will add 'event listeners' to the page that do not do anything except delay the interaction to next paint (INP).  If your WordPress site is having issues with the Interaction to Next Paint caused by long processing time take the following steps:

  • Check the theme settings. Uncheck any unneeded options like 'smooth scroll' or ' animated menu'. Settings like these tend to cause INP issues.
  • Check which scripts are responsive for the long processing time (tip: use the Chrome performance panel). If those scripts are plugin-related consider finding another plugin that does roughly the same
  • Often times there are custom scripts running on the page. Check those scripts and make sure that they yield to the main thread often and wrap less important callbacks in a requestIdleCallback function
  • Unload unused scripts on a per-page basis (tip: use wp_deregister_script). Some plugins tend to inject scripts on every page even when the functionality is not needed.
  • Check your Tag manager and remove unused or unneeded tags.
  • Use lean and clean themes. Often times multi purpose themes that 'do everything' tend to have more scripts
  • Avoid page builders since they often rely heavily on JavaScript for presenting pages to the end user

REACT / NextJS

Reacts hooks and features make it possible to reduce the INP processing time:

Prioritize User Interaction with React Concurrency Features:

React's concurrency features introduced in versions 16 and 18 provide hooks and mechanisms to optimize rendering for a smoother user experience, especially during input.

  • useTransition & startTransition: Mark non-critical updates for later rendering. This prevents large updates from blocking user interaction.
  • useDeferredValue: Split your UI into essential and less-critical sections. React can interrupt rendering of the less-critical parts for a more responsive experience.
  • useOptimistic: Show a temporary, optimistic state while asynchronous operations (like network requests) are ongoing. This keeps the UI responsive even during data fetching.

Suspense for Data Fetching (React 18+):

Suspense in React 18 can be used to improve INP (Interaction to Next Paint) by allowing the browser to prioritize user interactions and optimize rendering. While React 16 introduced Suspense for code splitting, React 18 extends this functionality to encompass data fetching.

  • A fallback component, like a loading indicator, displays while data loads.
  • Once data arrives, React resumes rendering the suspended component.
  • Suspense, combined with interruptible rendering in Concurrent React, prioritizes user interactions. If a user interacts with a suspended component, React prioritizes rendering that component, maintaining responsiveness.

Overall, these features work together to ensure React prioritizes user interactions and avoids blocking the UI during updates or data fetching.


    I help teams pass the Core Web Vitals:

    lighthouse 100 score

    A slow website is likely to miss out on conversions and revenue. Nearly half of internet searchers don't wait three seconds for a page to load before going to another site. Ask yourself: "Is my site fast enough to convert visitors into customers?"

    Interaction to Next Paint - Processing TimeCore Web Vitals Interaction to Next Paint - Processing Time