Fixing INP Problems Caused by Third-Party Scripts (GTM, Chat Widgets, Cookie Banners)
INP is the most-failed Core Web Vital, roughly 4 in 10 sites miss the 200 ms threshold, and when we audit a failing site, the culprit is rarely the site's own code. It's the accumulated layer of third-party scripts: the tag manager carrying 40 tags, the chat widget shipping half a megabyte of JavaScript to render one button, the consent platform that detonates a script pileup the moment someone clicks "Accept".
The frustrating part: your Lighthouse score can be green while all of this is happening, because lab tests can't measure INP at all, there's no real user in a lab test to click anything. If your lab score and field assessment disagree, that's why.
TL;DR
- INP fails when JavaScript is hogging the main thread at the moment a user clicks, taps, or types. Third-party scripts are the most common source of that blocking.
- The big four offenders: chat widgets (fix: facade pattern), GTM tag pileups (fix: trigger discipline + tag audit), cookie consent platforms (fix: defer the post-consent script burst), and session-replay/heatmap tools (fix: sample or remove).
- Diagnose with the
web-vitalsattribution build and the Performance panel's long-task view, not with Lighthouse, which cannot see INP. - Every fix is verified in field data only. Ship, measure with RUM for a few days, then let the 28-day CrUX window catch up.
How a Script You Didn't Write Ruins Your INP
INP measures the slowest meaningful interaction on the page: the gap between a click/tap/keypress and the next frame the browser paints. It splits into three phases, and third-party scripts attack all three:
| Phase | What happens | How third parties inflate it |
|---|---|---|
| Input delay | The click waits for the main thread to be free | A long task (>50 ms) is already running, analytics batching, tag firing, widget hydration |
| Processing | Event handlers run | Consent-triggered scripts, tracking calls attached to your buttons, widget listeners |
| Presentation delay | The browser renders the next frame | Late script-driven DOM work competing with the paint |
The main thread is the key concept: the browser has exactly one, and every third-party script shares it with your click handlers. A 300 ms analytics task doesn't feel slow in the abstract, until it happens to be running at the instant a user taps "Add to basket", and the tap sits in a queue behind it.
First: Find the Actual Culprit
Don't fix by guesswork, third-party INP damage is invisible in lab tools, so you need field-side evidence.
- Install the
web-vitalsattribution build. It reports not just your INP value but the element interacted with, the interaction type, and the longest script during the interaction. Feed it to GA4 or any endpoint. Within days you'll know which interactions fail and which script was blocking. - Reproduce in DevTools. Performance panel, enable 4× or 6× CPU throttling, record while clicking your key interactive elements. Long tasks show as red-flagged blocks; the flame chart attributes each one to its source script.
- Confirm by subtraction. Use request blocking (DevTools or WebPageTest) to block a suspect domain, re-record, and compare. If the long tasks vanish with the chat widget blocked, you have your answer, and a number to put in front of whoever owns that widget.
The Usual Suspects, and the Fix for Each
1. Chat widgets (Intercom, Zendesk, Drift, HubSpot, Tidio)
The typical live-chat embed downloads 300–800 KB of JavaScript on page load to render a button that most visitors never click. It hydrates on your main thread, attaches global listeners, and keeps working in the background.
The fix is the facade pattern. Render a lightweight fake, a static button styled identically to the real one, and only load the actual widget when someone clicks it. The first click carries a short load delay (acceptable; the user has signalled intent), and every other interaction on the page stops paying the tax. If a facade isn't an option, at minimum delay the widget until first user interaction or a few seconds after load, never load it render-blocking on page view.
2. Google Tag Manager (the tag pileup)
GTM itself is a modest script. The problem is what teams put inside it: years of accumulated tags, all firing on "All Pages" at page view, each one a main-thread task.
- Audit and delete. Export the container and review every tag. On most mature containers we audit, 20–40% of tags reference dead campaigns, retired tools, or duplicate trackers. Deleting them is free INP.
- Retime the rest. Change non-critical triggers from Page View to Window Loaded, or to custom events that fire on first interaction. Nothing about a remarketing pixel needs to run before the page is usable.
- Consider server-side GTM for the heaviest trackers. Moving tag execution off the browser removes the main-thread cost, at the price of infrastructure setup, usually worth it at scale.
3. Cookie consent banners (CMPs)
Consent platforms hurt INP twice. The banner itself is often a heavy iframe-and-script bundle that loads early because it legally must. Worse is the moment the user clicks "Accept": the CMP releases every gated script at once, analytics, ads, pixels, and that stampede lands on the main thread precisely during an interaction. It's why the consent click itself is frequently a site's worst recorded INP event.
Fixes: load the CMP async (it must be early, it needn't be blocking); after consent, stagger the released scripts instead of firing them synchronously, a queue that yields between each script keeps any single task under 50 ms; and audit what's gated, because the post-consent burst is your GTM tag pileup wearing a legal disguise.
4. Session replay, heatmaps, and A/B testing tools
Hotjar, Clarity, FullStory, and A/B platforms are the stealth offenders: they instrument every interaction by design, so they add processing cost to every interaction. A/B testing tools are worse again because they often run synchronously early to prevent content flicker.
Fixes: sample aggressively (record 5–10% of sessions, not 100%), run replay tools only on pages you're actively studying, and re-evaluate whether each tool still earns its cost, the honest answer for a heatmap tool installed in 2023 is usually no.
Two Techniques That Help Across the Board
Yield to the main thread. Where you control the code that runs on interaction, break long tasks up. scheduler.yield() (with a setTimeout(..., 0) fallback) lets the browser paint between chunks, the user sees a response even while work continues. This won't fix a bloated third party, but stops your own handlers from compounding one.
Web workers via Partytown, with caution. Partytown relocates third-party scripts to a worker thread, taking them off the main thread entirely. When it works, results are dramatic. But compatibility varies by script, widgets that heavily touch the DOM struggle, so treat it as a targeted tool for analytics-style scripts, tested per script, not a blanket solution.
Frequently Asked Questions
How do I know if third-party scripts are causing my INP problems?
Use the web-vitals attribution build in the field or the DevTools Performance panel with CPU throttling in the lab. Both attribute long main-thread tasks to their source script. If the worst tasks trace to domains you don't own, that's your answer.
Why is my INP bad when my Lighthouse score is 90+?
Lighthouse cannot measure INP, it needs a real user interaction. Its TBT proxy only covers page load. A page can score 95 and still fail INP from a chat widget that blocks the thread on click.
Will removing a chat widget improve INP immediately?
For real users, yes, instantly. In your Core Web Vitals report, gradually: the CrUX window takes up to 28 days to fully reflect the change. First movement shows within 2–3 days.
Is Google Tag Manager bad for Core Web Vitals?
GTM itself is manageable; unaudited containers are the problem. A disciplined container with correct trigger timing has a modest cost. Sixty tags firing at page view will fail INP on mid-range mobiles almost by itself.
Failing INP Across Thousands of URLs?
Third-party INP failures are fixable without ripping out your marketing stack, it's about what loads, when, and on whose thread.