In March 2024, Interaction to Next Paint replaced First Input Delay as a Core Web Vital, which means that the Core Web Vitals will consist of:
- Interaction to Next Paint (INP)
- Cumulative Layout Shift (CLS)
- Largest Contentful Paint (LCP).
Let’s see how these metrics are calculated, and see what we can do to optimize our apps for improved performance and Google ranking in 2024.
Interaction to Next Paint (INP) is the total score of three delays:
- Input Delay: the time between user interaction and the time the browser can process the event.
- Processing Delay: the time it takes the browser to process the event handlers.
- Presentational Delay: the time it takes the browser to recalculate the layout and paint the pixels to the screen.
The INP score is measured when the user leaves the page by aggregating all interactions the user made with the page throughout the page’s lifetime and returning the worst-measured score.
With INP, it’s crucial to address the entire lifecycle of a user interaction. This includes processing event handlers, recalculating layouts, and painting updates to the screen, all of which are critical components of the INP metric.
To optimize the INP score, we need to consider a broad range of performance factors.
1. Minimize processing delay
The processing delay emphasizes the importance of not only starting the event handler quickly but also executing it efficiently. We can optimize the processing delay by:
- Profiling code and identifying performance bottlenecks.
- Using techniques like
debounce
orthrottle
for frequently firing event handlers. - Code splitting and tree shaking to reduce unnecessary JavaScript. Load only what is necessary, when it is necessary.
- Breaking down long JavaScript tasks into smaller chunks. The longer a task takes on the main thread, the longer it will block user interactions.
2. Minimize presentational delay
After processing an event, the browser may need to recalculate styles, reflow layouts, and repaint the screen. We can optimize the presentational delay by:
- Using efficient styling and layouts, such as using the
will-change
property judiciously to inform the browser about properties and elements likely to be animated. We can also opt fortransform
andopacity
changes for animations as they are less likely to cause layout recalculations, and use properties likecontent-visibility
to render and update content only when necessary - Reducing forced synchronous layouts. Avoid reading layout properties immediately after writing them, as this can trigger the browser to synchronize and recalculate styles and layouts.
- Using web forkers for non-urgent and non-UI tasks. By offloading these tasks to background threads, you can keep the main thread free and responsive to user interactions.
3. Optimize event handlers
Event handlers should be executed efficiently and effectively. We can do this by:
- Deferring non-critical events until the main thread is less busy.
- Using passive event listeners for events like
scroll
andtouch
. This informs the browser that the event listener will not prevent a scroll or touch event, allowing the browser to continue smooth scrolling without waiting for the listener. - Delegating events by, instead of attaching event listeners to individual elements, attaching them to a common parent and using event properties to determine which child element was interacted with. This reduces the number of active event listeners and potential slowdowns.
4. Utilize React 18's concurrent features
React 18’s concurrent features introduce several enhancements that can significantly improve INP scores.
- Concurrent Rendering: This allows React to prepare multiple versions of the UI simultaneously. This is especially beneficial for INP, as it enables smoother transitions and less noticeable delays during complex updates.
- Automatic Batching: React 18 automatically batches multiple state updates into a single re-render, reducing the number of renders. This minimizes Processing Delays by ensuring that frequent state changes result in fewer, more efficient re-renders.
- Suspense for Data Fetching: This new feature lets components wait for something before rendering, such as data from an external source. It helps in managing loading states more efficiently, reducing the Presentational Delay by avoiding unnecessary layout recalculations and repaints until data is fully ready to be displayed.
- Transition API: This API allows for certain updates to be marked as non-urgent. By distinguishing between urgent and non-urgent updates, React 18 helps in maintaining responsiveness even when the app is performing heavy tasks, thus optimizing the INP score.
Cumulative Layout Shift (CLS) is an important performance metric that captures the cumulative score of all unexpected layout shifts that occur during the entire lifespan of a page. These unexpected shifts can disrupt the reading experience, leading users to click on something they didn't intend to. Elements such as images, web fonts, dynamic content (like cookie banners), and ads are common culprits.
The CLS score is derived by multiplying two factors for every unexpected layout shift:
- Impact Fraction: The portion of the viewport that was shifted during the layout change.
- Distance Fraction: The distance the unstable elements have moved, relative to the viewport largest dimension (usually the height).
Suppose a banner loads and pushes content by 22% of the viewport height, and it impacts the entire viewport.
This layout shift would result in a score of 0.22 (22% distance fraction) * 1 (100% impact fraction) = 0.22.
Now if we suddenly loaded an image in this paragraph, the affected area is much less since the upper part of the page hasn’t shifted.
It moved by a greater percentage of the viewports height, but it impacted a small part of the viewport, so the total CLS is only 0.15 (distance) * 0.30 (impact) = 0.045.
Remember, the "cumulative" in CLS means the metric considers the sum of all individual layout shifts that occur throughout the entire page's life.
Ideally, you want to keep the CLS score below 0.1.
1. Set Explicit Dimensions
Define sizes for visual elements and dynamic content containers. This approach helps to avoid unexpected layout shifts when elements are loaded or when their content changes. For images, videos, iframes, ads, embeds, and other dynamically sized content, setting explicit width and height attributes allows the browser to allocate the correct amount of space while the content is loading.
2. Load and render fonts performantly
Use correct font-display
values and preload essential web fonts. This technique reduces layout shifts due to font loading. By specifying font-display: swap;
in your CSS, you can control how long a browser waits for a web font to load before using a fallback font. Preloading important fonts can also speed up their display and reduce layout shifts.
3. Choose Performant Animation Techniques
Use transforms over properties that trigger layout recalculations. Animating properties like width
, height
, or margin
can cause significant layout shifts, leading to poor CLS. Instead, use properties like transform
and opacity
for animations, as the browser can optimize them and not trigger layout recalculations, leading to smoother animations with less impact on layout stability.
Largest Contentful Paint (LCP) measures how long it takes for the main content of your web page to load and become visible to the user. Large images, video poster images, or text blocks within the viewport typically represent this.
The sooner users can view and interact with your page's main content, the better their experience. A delayed LCP could mean a user is staring at a blank screen or an incomplete page, leading to a perception of poor performance and possibly increasing the chance of abandoning the site.
LCP is the render time of the largest content element visible in the viewport. This could be:
- An image or video
- A block-level element containing text
- Any other content type that takes up a significant amount of viewport space
The largest element is determined by its visible size in the initial viewport. Note that this may not be the largest element on the entire page!
A good LCP time is around 2.5 seconds or faster for the 75th percentile of page loads.
1. Use modern image formats like WebP
WebP images are significantly smaller in size compared to traditional formats like JPEG or PNG, which means they can be downloaded and rendered faster, thus improving LCP.
2. Preload crucial assets
Preloading allows you to instruct the browser to fetch critical resources, such as key scripts, stylesheets, or fonts, early in the page load process. This ensures that these resources are available as soon as they are needed, reducing the time it takes to display the largest element of the page.
ReactDOM
methods to safely insert these values into the <head> of the document.
3. Optimize server response times
Decrease latency by upgrading server hardware, using a CDN, or optimizing your server-side code. Faster server responses mean quicker data availability for rendering the page, thus improving LCP.
4. Minimize and defer non-critical CSS and JavaScript
Minimizing CSS and JavaScript means removing unnecessary code, and deferring non-critical scripts ensures they don’t block the rendering of the main content. This can be achieved by code splitting components that are not essential for the initial page load.
5. Use efficient caching policies
Ensure that users returning to your site have the resources loaded from the cache, significantly reducing load times.
To measure you're website’s INP, CLS and LCP score, you can use