How is INP with Client-Side Routed Links in SSR Frameworks like Next.js/Nuxt/Remix (...) measured

Skip to first unread message

Jan Nicklas

Apr 15, 2024, 4:17:18 AMApr 15
to web-vitals-feedback
I’ve been following the discussion on how INP (Interaction to Next Paint) is measured (

According to Michal Mocny INP measures the click interaction itself rather than waiting for the network reply or the new page to load.
This makes sense for traditional hyperlinks that load a new page within the same window.

However, in frameworks like Next.js, once a page is hydrated, hyperlink clicks are generally managed by a client-side navigation router. This means the default hyperlink action is prevented, and navigation is handled entirely in JavaScript. The framework starts a network request in the background, waits for the data, and then updates the DOM without a full page refresh.

Could someone please clarify how INP is tracked in such a scenario?
Is there a difference in how INP is measured between a static HTML hyperlink and a hyperlink controlled by a client-side router, such as in Next.js?

Barry Pollard

Apr 15, 2024, 4:49:18 AMApr 15
to web-vitals-feedback
An important point to realise is that INP is currently based on the next paint, not the next contentful paint.

While ideally those two would be the same thing, some responses will naturally take some more time (especially where a network request takes place—which is usually asynchronous so non-blocking). The metric is designed to encourage sites to avoid blocking the main thread and so blocking any feedback from being able to happen, rather than measuring the whole end to end impact of the interaction. The next paint is the first opportunity to present some feedback, and feedback can include intermittent bits of feedback provided by the site ("Processing..." messages, page loading bars shown by some SPAs, spinners...etc.), but also smaller pieces of browser feedback (e.g. button 3D UI effects), and in some occasions no feedback at all—but the main thread is free to give feedback and handle other critical interactions if necessary.

This means that in both SPA and MPA scenarios, INP is not (necessarily) measured up until the next full page load displays content, but only up until the main thread is no longer being blocked.

INP is attributed to the full browser-controlled page (i.e. the one that is shown in the URL before the client-side JavaScript takes over), so that means the the following:
  • For SPAs, each click is measured until the next possible paint, and included in the original landing page's INP score. Attributing all INP to the original landing page is not ideal and work is ongoing to try to address this. As I said above, that does not necessarily mean it measures from click until the full soft page load happens, but from click until the browser is next able to paint a frame. So if an asynchronous network request is need, then this will not be included in INP as the next paint opportunity should be while that is happening. In that case the full page loads is not measured at all by any Core Web Vitals at present but ideally it would be measure by the LCP of the next "page" load. However, if a network request is not required for the navigation, the whole next page load may be blocking if the JavaScript does not yield (either in your code, or in the SPA framework) while it is processing, so INP may be measuring the whole click to full presentation of the next page in this case.
  • For MPAs, it's a little more complicated. Ideally we'd measure this from link click until the page is unloaded, however often that has no final paint (unless there is a significant event handler attached to the link click) so simple link clicks are ignore for INP in those  cases at present. However, those types of simple links are rarely INP issues anyway so ignoring them is fine. The full page load is measured by LCP for the next page (from click until LCP is shown) rather than by INP.

Reply all
Reply to author
0 new messages