DEV Community

Cover image for How We Reduced INP by 100ms+: GTM Isolation, React Compiler, and Better Telemetry
Francesca Milan for Subito

Posted on with Alessandro Grosselle • Edited on

How We Reduced INP by 100ms+: GTM Isolation, React Compiler, and Better Telemetry

At Subito (Italy's leading classifieds platforms), we constantly monitor our Core Web Vitals. While we had a handle on LCP and CLS, we were constantly struggling with INP (Interaction to Next Paint) on our high-traffic public pages, specifically our Listing and Ad Details pages.

Unlike LCP or CLS, where we have automated alerts triggering when thresholds are breached, setting up alerts for INP seemed impossible.

What is INP?

For context, INP measures a page's responsiveness. It observes the latency of all user interactions (clicks, taps, and key presses) throughout the lifespan of a user's visit. The final value is the longest interaction observed (ignoring outliers).

According to Google's standards, a "Good" experience is defined by strictly defined thresholds:

  • 🟢 Good: 200ms
  • 🟡 Needs Improvement: Between 200ms and 500ms
  • 🔴 Poor: 500ms

INP is about how "fast" the site feels when you try to use it. A poor INP means the user clicks a button and... waits.

The Problem: "It's Not Just Us"

We struggled with INP because fluctuations weren't always caused by our code.

On our highest-traffic pages, two major actors are almost entirely out of our direct control:

  1. GTM (Google Tag Manager): Managed by our Marketing team.
  2. ADV (Advertising): Managed by our Sales/Advertising team.

Both inject code and event listeners that heavily impact performance.

The Old "Empiric" Way (And Why It Failed)

Previously, our monitoring was purely observational via Grafana. When INP spiked, we would scramble to check recent releases. Most of the time, our code changes weren't the culprit, and rollbacks did nothing.

We resorted to an empiric, frustrating debugging process: open Chrome DevTools, throttle the CPU to 4x, simulate a 4G network, and click around hoping to reproduce the lag. It wasn't wrong, but it was inefficient and lacked engineering rigor.

The "Scientific" Approach: Isolating the Actors

This year, the Frontend Chapter decided to stop guessing. We needed to measure the specific weight of the three actors: Our Code, GTM, and ADV.

With approval from the respective teams, we ran an A/B test on 1% of our traffic:

  • Segment A: Loaded without GTM.
  • Segment B: Loaded without ADV.
  • Segment C: Standard traffic.

This finally gave us clear baselines.

Case Study 1: The Ad Details Page

Here is what we found for the Ad Details page:

  • Standard INP: 208ms (Needs Improvement)
  • INP without ADV: 180ms
  • INP without GTM: 112ms (Good!)

INP Data Ad Details

INP Graph Ad Details

The data was undeniable: GTM was the primary bottleneck.

Case Study 2: The Listing Page

The Listing page was more complex. Even without external scripts, our baseline was high:

  • Standard INP: 345ms (Very Poor)
  • INP without ADV: 279ms
  • INP without GTM: 320ms

INP Data Listing

Here, removing GTM didn't help much, and removing ADV helped but didn't solve it. We had to dig deeper.

Solving the GTM Issue (Ad Details)

Once we knew GTM was the culprit on the Ad Details page, we collaborated closely with the Marketing team.

GTM works via triggers (events) that inject JavaScript (tags). To find the needle in the haystack, we cloned the GTM workspace for our 1% traffic slice and used a "Bisect" approach (binary search):

  1. We disabled 50% of the triggers.
  2. Monitored the INP.
  3. If improved, the issue was in that 50%. If not, we checked the other half.
  4. Repeat until the specific script is found.

The Verdict: The heavy hitters were tracking scripts for TikTok and Facebook.

Instead of complex engineering workarounds to move these scripts out of GTM, the Marketing team simply agreed to disable the TikTok tracking.

Result: INP dropped from 208ms to ~170ms. We were finally under the 200ms threshold! 🎉

INP Drop Ad Details

Solving the Complex Issue (Listing Page)

The Listing page was harder because there was no single "culprit."

Step 1: Better Telemetry with Grafana Faro

We integrated Google's web-vitals library to capture attribution data (longestScriptURL, interactionTarget) and sent it to Grafana Faro:

getFaro().api.pushMeasurement(
    {
      type: 'INP',
      values,
    },
    {
      context: beacon,
    }
);
Enter fullscreen mode Exit fullscreen mode

This allowed us to visualize exactly which scripts and which DOM elements were causing delays.

Grafana Faro Logs

We discovered that clicks on a.index-module_link were problematic. These links had heavy event handlers attached by interstitial ads (full-page ads).

DOM Element Analysis

We are still negotiating a fix with the ADV team, but we couldn't just wait.

Step 2: Enter the React Compiler

To optimize the code we did control, we decided to upgrade to Next.js 16 and enable the React Compiler.

The upgrade process wasn't difficult (though switching from Webpack to Turbopack was an adventure for another article).

The Result:
Surprisingly, just enabling the React Compiler significantly dropped our INP:
From 345ms down to 271ms.

React Compiler Result

It's still not perfect, but it was a massive free performance win. If we calculate the "No ADV" scenario combined with React Compiler, we would actually be under the threshold:

React Compiler No ADV

Conclusion

We aren't done yet, but this journey taught us valuable lessons:

  1. Don't Guess, Measure: Isolate your third parties (GTM, ADV) using traffic splitting (even 1-2%). It gives you leverage and data to drive decisions.
  2. React Compiler is Legit: It's not hard to introduce, and it can provide a significant performance boost for free.
  3. Collaborate, Don't Hack: We avoided clever technical workarounds (like the DataLayer yield pattern). Why? Because talking to the Marketing team and solving the root cause is sustainable. Hacks are not.

Next Steps

  • Enable automated alerting for INP now that we understand the baseline.
  • Continue optimizing the Listing page to reach the green threshold (sub 200ms).
  • The Big Goal: Treat Core Web Vitals as business metrics. We want to establish "Performance Budgets" (e.g., GTM gets max 50ms, ADV gets max 50ms) to ensure quality isn't sacrificed for tracking.

Top comments (2)

Collapse
 
ardiaankur profile image
Ankur Gupta

for tracking core web vitals which one is better as per your perspective Grafana faro using web vitals library or Amplitude using the same library?

Collapse
 
alessandro-grosselle profile image
Alessandro Grosselle Subito

In my opinion, the specific tool you choose is not the key point. What really matters is having the right information when INP exceeds the threshold: which element was clicked and what is blocking the main thread.