Investigating INP issues | Stefan Judis Web Development

I’ve been doing some client performance work lately. It’s a Next.js project, and it looks like Google’s INP update has punished it. Organic search rankings are tanking.

I thought it would be a quick gig because there was a ton of low-hanging fruit. After fixing, waiting and constantly checking the Search Console (it takes a while until Googlebot recognizes improvements), things moved in the right direction, but fixing the obvious isn’t enough.

Many “need improvement” URLs moved to “good” in May, but “good” URLs still don’t make up the majority.

Entries showing URLs that aren't considered "good". 161 of URLs have an INP issue. And 41 have an LCP issue.

The main offender is the new INP metric, and that’s no surprise. The site’s built with a JS-heavy stack: many libraries, huge hydration data blocks, SVGs in React — you get the idea. A lot is going on in modern web development.

I still need to solve the INP issue, but I learned a few things.

The web-vitals Chrome can log to the console

After spending time in Lighthouse and the Chrome performance panel, I figured that the official Chrome extension might be worth a shot. Once you install it, you’ll see some juicy performance metrics right away. Nice!

But opening the extension can be a bit cumbersome, especially when trying to nail down slow interactions. To tackle INP and unresponsiveness, you can’t just run Lighthouse and call it a day. INP relies on real interactions; you must “do stuff” and measure.

Luckily, the extension has a solution to this problem — console logging.

Browser window with the web vitals extension installed. After enabling the “console logging” option all perf metrics are logged to the JS console.

I was surprised by the details logged to the console.

console entries showing interaction target, interaction event type and timings.

Interaction details, the user interaction target and type — all this info is right there in the console. But where’s this data coming from?

web-vitals.js has a new(?) attribution package

Internally, the Chrome extension relies on the official Web Vitals package. And this one now comes with a new attribution module.



import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         
  console.log(value);        
  console.log(rating);       
  console.dir(attribution);  
});

As far as I can see, web-vitals.js uses PerformanceObserver magic to squeeze out and format the performance and attribution details. If you inspect the code, it’s wild but good stuff.



import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; 

  
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    
    const {invokerType} = script;        
    const {invoker} = script;            
    const {sourceURL} = script;          
    const {sourceCharPosition} = script; 
    const {sourceFunctionName} = script; 
  }
});

You can see which script was handling which interactions, too. Great!

I could now set up some RUM monitoring to find the actual performance offender in my current project, but I hope to resolve the issue with some clicky-looky myself. Fingers crossed.

If you want to learn more, the Chrome developer relations team published some great materials:

Until then, I’ll report back when I fixed the issues.