Optimizing Core Web Vitals in 2024

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

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.
A single Interaction to Next Paint measurement is the total of three delays combined
A single Interaction to Next Paint measurement is the total of three delays combined

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.

Optimizing INP

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 or throttle 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 for transform and opacity changes for animations as they are less likely to cause layout recalculations, and use properties like content-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 and touch. 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.
Next.js App Router leverages all of React 18's concurrent features out-of-the-box, resulting in more performant page loads and navigations.

Cumulative Layout Shift

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:

  1. Impact Fraction: The portion of the viewport that was shifted during the layout change.
  2. 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.

Group 513652 (2).png
Group 513652 (2).png

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.

Group 513652 (3).png
Group 513652 (3).png

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.

Optimize CLS

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.

Next.js provides an optimized Image component that helps in managing layout shifts effectively. It helps set the correct dimensions, optimizes images on-the-fly, supports various image formats, and automatically adjusts image quality based on the user's device. This not only aids in maintaining layout stability but also enhances performance and loading times. Moreover, its built-in lazy loading feature only loads images as they enter the viewport, further optimizing the user experience without causing layout shifts.

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.

Leveraging Next.js’s Font module can significantly improve font loading and rendering. This component automates the preloading of custom fonts, ensuring they are available early. This reduces the chance of text rendering delays and prevents layout shifts when the font switches from the fallback to the web font.

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.

The Vercel Toolbar automatically detects and replays layout shifts on your deployments. Learn more about automatic layout shifts detection.

Largest Contentful Paint

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!

Optimize LCP

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.

Next.js provides an optimized Image component that supports various image formats, and automatically adjusts image quality based on the user's device.

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.

Learn more about how to use new 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.

Leveraging the Vercel Edge Network can significantly decrease server response times.

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.

By default, Next.js will cache as much as possible to improve performance and reduce cost. Learn more about caching in Next.js.

Measuring Core Web Vitals

To measure you're website’s INP, CLS and LCP score, you can use

Couldn't find the guide you need?