Call or WhatsApp: +92 321 209 8738 WhatsApp: +971569803329 info@pixelsproagency.com

Core Web Vitals

How to Fix a Poor INP Score Caused by Chat Widgets and Third-Party Scripts

If PageSpeed Insights or Search Console has flagged your Interaction to Next Paint (INP) as poor, the cause is often not your own code. On small business websites the usual suspects are the live chat widget, a heatmap tool, a stack of tracking tags and a few embeds, all fighting for the browser's attention at the moment a visitor taps your menu or your "Get a quote" button. This guide explains how to improve your INP score without rebuilding your site: how to confirm the problem with real-user data, find the slow interaction, load the chat widget only when someone wants it, trim third-party scripts, fix your own click handlers, and measure INP yourself in GA4. All code was tested before publishing.

What INP Measures, in Plain Terms

Interaction to Next Paint, or INP, measures how long your page takes to show a visible response after someone clicks, taps or presses a key. Google made it a Core Web Vital in March 2024, when it replaced First Input Delay. The difference matters. First Input Delay only looked at the very first interaction and only at the waiting part. INP watches every click, tap and key press for the whole visit and reports one of the slowest. For most pages that is simply the worst interaction, because Google only starts ignoring outliers once a page has more than 50 interactions.

Google's INP documentation on web.dev sets the thresholds at the 75th percentile of real visits, measured separately for mobile and desktop:

INP thresholds (75th percentile of real page visits)
INP valueRatingWhat a visitor feels
200 ms or lessGoodMenus, tabs and buttons react straight away.
Over 200 ms, up to 500 msNeeds improvementA noticeable lag. People tap twice or wonder whether the button worked.
Over 500 msPoorThe page feels frozen. Taps get lost or land on the wrong thing.

Every interaction has three parts, and knowing which one is slow tells you where to look:

  • Input delay. The browser is busy with something else, often a third-party script, when the tap arrives, so your click handler can't start yet.
  • Processing duration. Your own event handlers, plus any other listeners attached to the same click, take too long to run.
  • Presentation delay. The handlers have finished, but the browser still has to recalculate styles and layout and paint the new frame. Huge DOM changes make this part slow.

One detail catches people out. Interactions inside iframes count towards your page's INP, because the visitor can't tell what's an iframe and what isn't. Many chat widgets, booking tools and video players live in iframes, so their slowness becomes your score.

Why Chat Widgets and Tag Scripts Hurt INP So Often

Small business websites in Pakistan and the UAE tend to carry the same stack: a live chat or WhatsApp widget, the GA4 tag, a Meta Pixel, sometimes a heatmap tool, a cookie banner, a reviews badge and a Google Map. Each one is reasonable on its own. Together they compete for the same main thread that has to respond to your visitor's taps.

Live chat widgets are the usual worst offender, for three reasons:

  1. They load on every page, for every visitor, even though only a small share of visitors ever open the chat. All of them pay the cost of downloading and running it.
  2. They do a lot of work early. A typical widget loads its script, then fetches settings, opens a connection to its servers, injects an iframe and styles, and sometimes plays an entrance animation. If a visitor taps your menu while that's happening, the tap waits. That is input delay.
  3. They keep running. Some poll for new messages, track scrolling or listen to every click on the page. Those listeners add processing time to interactions that have nothing to do with chat.

The good news is that third-party problems are usually the cheapest INP problems to fix. You don't need to rewrite your site. You need to control when those scripts run.

Step 1: Find Out Whether You Really Have an INP Problem

Start with real-user data, not a lab score. Open PageSpeed Insights and test your most important page. The top section, "Discover what your real users are experiencing", comes from the Chrome User Experience Report and covers the previous 28 days. If it shows INP, that is the number Google uses.

Three situations are common on small sites:

  • There's no field data at all. Your site doesn't have enough Chrome traffic to appear in the report. You'll need to measure INP yourself, which we cover below.
  • There's data for the whole site (the origin) but not for the URL. Fine for a start. It tells you whether the problem is site-wide, which usually points to a script on every page.
  • Mobile is poor and desktop is fine. Very typical. Mid-range Android phones have far less processing power than a laptop, and the same script that takes 40 ms on a laptop can take several times longer on a budget phone.

In Google Search Console, the Core Web Vitals report groups similar URLs together, which helps you see whether the issue is one template (say, all blog posts) or everything. If you haven't set up Search Console yet, our Search Console guide for beginners walks through it.

Be careful with the Lighthouse score in PageSpeed Insights. A standard Lighthouse run only loads the page; it doesn't click anything, so it can't report INP. The closest lab number is Total Blocking Time, which Google itself describes as a reasonable proxy but not a substitute. A page can score 95 in Lighthouse and still have a poor INP if the slow part is a menu or a filter that only runs when tapped.

Step 2: Reproduce the Slow Interaction in Chrome

Once you know which page is slow, find out which interaction is slow. In Chrome DevTools, open the Performance panel. Recent versions of Chrome show live Core Web Vitals for the page you're on, including INP, and list each interaction as you make it. Two settings make the test realistic:

  • Turn on CPU throttling (4x or 6x slowdown) so your laptop behaves more like a mid-range phone.
  • Interact while the page is still loading, not after it settles. Google recommends this because the main thread is busiest during load, and that's when visitors tap the menu or the WhatsApp button.

Then try the things real visitors do: open the mobile menu, tap a tab or accordion, open the chat bubble, start typing in the contact form, accept the cookie banner. When one interaction shows a high number, record a performance trace while repeating it. Look at the long tasks around the interaction and check which script they come from. If the long task belongs to widget.js from your chat vendor, or to a tag manager container, you have your culprit.

A quick test that needs no tools: in DevTools, open the Network panel, right-click the chat vendor's script and choose to block that request URL, then reload. If the menu suddenly feels instant on a throttled CPU, the widget is costing you.

Step 3: Load the Chat Widget Only When Someone Wants to Chat

The most effective fix for chat widgets is a facade: a lightweight button that looks like the chat bubble but is just plain HTML and CSS. The real vendor script only loads when someone taps it. Visitors who never chat never pay for it, and that's usually most of them.

Here's a small, vendor-neutral version. Replace the data-chat-src value with the script URL from your chat provider's install code, and check the provider's documentation for how to open the chat window from JavaScript.

<button type="button" class="chat-launcher" id="chat-launcher" data-chat-src="https://embed.example-chat.com/widget.js">
  Chat with us
</button>
<script>
(function () {
  var btn = document.getElementById('chat-launcher');
  if (!btn) return;
  var requested = false;

  function loadChat() {
    if (requested) return;            // never inject the vendor script twice
    requested = true;
    btn.textContent = 'Opening chat...';
    btn.setAttribute('aria-busy', 'true');

    var s = document.createElement('script');
    s.src = btn.getAttribute('data-chat-src');
    s.async = true;
    s.onload = function () {
      btn.hidden = true;              // the vendor's own bubble takes over
      // Most vendors expose an "open" call; check your vendor's docs for the exact name.
      if (window.ChatVendor && typeof window.ChatVendor.open === 'function') {
        window.ChatVendor.open();
      }
    };
    s.onerror = function () {
      requested = false;
      btn.removeAttribute('aria-busy');
      btn.textContent = 'Chat unavailable, try WhatsApp';
    };
    document.head.appendChild(s);
  }

  btn.addEventListener('click', loadChat);
})();
</script>

We tested this in jsdom, a headless browser environment. No vendor script is requested before the button is tapped. Tapping it twice still injects the script only once. If the script fails to load, the button says so instead of silently doing nothing, and a second tap retries. When the script loads, the placeholder hides and the vendor's own open function is called if it exists.

A few practical notes:

  • The first open is slower. The script has to download when tapped, so the chat window may take a second or two to appear. The "Opening chat..." text tells the visitor something is happening, which is exactly what INP rewards: a visible response in the next frame.
  • Unread-message badges and proactive pop-ups stop working for visitors who never tap. For most service businesses that's a fair trade. If your sales team depends on proactive chat, consider loading the widget only on the pricing or contact page instead of everywhere.
  • Keep a WhatsApp link as the backup. In Pakistan and the UAE, many customers would rather message on WhatsApp than use a website chat anyway. A plain wa.me link costs nothing in performance, and you can measure its taps with the method in our guide to tracking WhatsApp clicks in GA4.

Some chat providers offer their own "lazy load" or "load on interaction" setting. Check for one before writing code. Several WordPress chat plugins also include a delay option. Whatever the method, the principle is the same: don't run code for a feature the visitor hasn't asked for.

Step 4: Audit Every Other Third-Party Script

After the chat widget, list every script on the page. In DevTools, the Network panel filtered to JS shows them all; the domain column tells you which are third-party. For each, ask three questions: does the business still use it, does it need to be on every page, and does it need to load before the visitor interacts?

Common third-party scripts and what to do with each
ScriptTypical problemWhat usually works
Live chat widgetHeavy startup work on every page, iframe interactions count towards INPFacade that loads on tap, or load only on contact and pricing pages
Heatmap or session recordingListens to every click, scroll and mouse movementRun it for a few weeks to answer a specific question, then remove it
Google Tag Manager containerOld tags nobody remembers, several tags firing on every clickDelete unused tags and triggers; avoid "All Elements" click triggers unless needed
Embedded Google MapLarge iframe and scripts on loadStatic map image linking to Google Maps, or load the iframe with loading="lazy"
YouTube embedHeavy player loaded before anyone presses playThumbnail facade that swaps in the player on tap
Social feeds and review badgesSeveral scripts and fonts for a small visual elementStatic screenshot or text with a link to the source
Duplicate analytics tagsGA4 installed both directly and through GTM, or two Pixel codesKeep one installation of each

Duplicate tags are more common than you'd think. When a site has been touched by several freelancers over the years, it's normal to find GA4 installed twice, an old Universal Analytics snippet that hasn't collected data since 2023, and a Pixel from an ad agency the client stopped working with. Removing dead code is the safest INP fix there is.

Lighthouse in PageSpeed Insights has an audit called "Reduce the impact of third-party code" that lists third-party scripts by how long they blocked the main thread during load. It's a good shortlist, even though it only reflects load time and not interactions.

Step 5: Fix Your Own Click Handlers

Sometimes the slow code is yours: a mobile menu that rebuilds itself on every tap, a portfolio filter that re-renders 60 cards, or a form that validates everything on every key press. The fix is to do the visible part first, give the browser a chance to paint, and do the heavy part after.

// Give the browser a chance to paint between chunks of work.
function yieldToMain() {
  if (globalThis.scheduler && typeof scheduler.yield === 'function') {
    return scheduler.yield();
  }
  return new Promise(function (resolve) { setTimeout(resolve, 0); });
}

document.querySelector('.filter-bar').addEventListener('click', async function (e) {
  var btn = e.target.closest('[data-filter]');
  if (!btn) return;

  // 1. Visible feedback first, so the next paint shows something happened.
  btn.classList.add('is-active');
  btn.setAttribute('aria-pressed', 'true');

  // 2. Let that paint happen before the heavy part.
  await yieldToMain();

  // 3. The expensive work: filtering and re-rendering cards.
  filterCards(btn.dataset.filter);

  // 4. Analytics last; it doesn't need to block the visitor.
  await yieldToMain();
  if (typeof gtag === 'function') {
    gtag('event', 'filter_click', { filter: btn.dataset.filter });
  }
});

scheduler.yield() is available in Chromium-based browsers, which covers most mobile traffic in Pakistan and the UAE; the function falls back to setTimeout elsewhere. We tested the fallback path in jsdom: the button gets its active state immediately, while the filtering and the analytics event run afterwards, in that order. Replace filterCards with your own function.

Other changes that help with processing and presentation delay:

  • Don't run work on every key press. Validate a field when the visitor leaves it, not on every character.
  • Show and hide instead of rebuilding. Toggling a class on existing elements is much cheaper than creating new HTML.
  • Keep the DOM smaller. A page with thousands of elements takes longer to lay out after every change. Long mega menus and hidden carousels are common causes.
  • Avoid layout thrashing. Reading an element's size and then changing styles in a loop forces the browser to recalculate layout many times in a single tap.

Step 6: Measure INP on Your Own Site

If your site doesn't appear in the Chrome report, or you want to know which button is slow, measure INP from real visitors yourself. Google's open-source web-vitals library does the hard parts, such as handling the back/forward cache and reporting when the tab is hidden. Its attribution build also tells you which element was tapped and how long each of the three phases took. This snippet sends that to GA4 as a custom event:

<script type="module">
  import { onINP } from 'https://unpkg.com/web-vitals@6/dist/web-vitals.attribution.js?module';

  onINP(function (metric) {
    var a = metric.attribution || {};
    gtag('event', 'inp_report', {
      value: Math.round(metric.value),          // milliseconds
      metric_rating: metric.rating,             // good | needs-improvement | poor
      inp_target: a.interactionTarget || '(unknown)',
      inp_input_delay: Math.round(a.inputDelay || 0),
      inp_processing: Math.round(a.processingDuration || 0),
      inp_presentation: Math.round(a.presentationDelay || 0)
    });
  });
</script>

The field names match the type definitions in web-vitals version 6.2.3, the current release when we wrote this. Place it after your existing GA4 tag. In GA4, register inp_target and metric_rating as custom dimensions so you can build a report of slow interactions by element. Two cautions: loading the library from a public CDN adds another third-party request, so self-host the file if you can, and this only measures visitors whose browser supports the underlying API, which today means Chromium-based browsers.

Give it a couple of weeks of traffic before drawing conclusions. A single slow interaction on a cheap phone tells you little; a pattern across many visits tells you what to fix.

A Checklist You Can Work Through in an Afternoon

  1. Check field INP for mobile and desktop in PageSpeed Insights, and the Core Web Vitals report in Search Console.
  2. Reproduce slow interactions with 4x CPU throttling, tapping during page load.
  3. Block the chat script in DevTools and repeat the test, to see how much it costs.
  4. Replace the chat widget with a facade, or limit it to the pages where people actually chat.
  5. List every third-party script and delete anything unused or duplicated.
  6. Clean out old Tag Manager tags and broad click triggers.
  7. Swap map and video embeds for static previews that load the real thing on tap.
  8. Make your own click handlers show feedback first and do heavy work after a yield.
  9. Add the web-vitals INP report to GA4 and check it after two weeks.
  10. Re-check field data after 28 days, since that's the window PageSpeed Insights reports.

How Much Does INP Matter for SEO?

Honest answer: less than content, and more than nothing. Core Web Vitals are part of Google's page experience signals, but Google has said for years that great content can still rank with average page experience. Don't expect a jump in rankings from fixing INP alone.

The stronger argument is the visitor. When a tap on "Call now" or "Get a quote" seems to do nothing for half a second, people tap again, lose patience or leave. On a lead-generation site, the slow interactions are often exactly the buttons that make you money. And because most of the fixes in this guide involve removing or delaying code, your page usually gets lighter overall, which helps load speed too. Our broader website speed optimization guide covers images, caching and loading performance if INP isn't your only issue.

Frequently Asked Questions

What is a good INP score?

200 milliseconds or less at the 75th percentile of real visits is good. Between 200 and 500 milliseconds needs improvement, and anything over 500 milliseconds is poor. Google measures mobile and desktop separately.

Why does PageSpeed Insights show no INP for my site?

Field INP only appears when your site has enough Chrome traffic to be included in the Chrome User Experience Report. Lab tests in Lighthouse don't click anything, so they can't report INP; they show Total Blocking Time as a proxy. Measure INP yourself with the web-vitals library if you have no field data.

Do live chat widgets slow down a website?

Often, yes. Most load a script, settings and an iframe on every page for every visitor, and that work can delay taps on the rest of the page. Loading the widget only when someone taps a placeholder chat button removes that cost for everyone who doesn't chat.

Will removing Google Tag Manager fix INP?

Usually not on its own. The container itself is rarely the problem; the tags inside it are. Delete tags and triggers you no longer use, avoid tags that fire on every click, and make sure analytics isn't installed twice.

How long does it take for INP improvements to show in Google's data?

PageSpeed Insights and Search Console use a rolling 28-day window of real visits, so improvements appear gradually and fully after about four weeks. Your own web-vitals reporting in GA4 shows changes within days.

Is Your Site Slow to Respond?

Send us the page that feels sluggish and the scripts you rely on (chat, analytics, ads). We can review what each one costs and suggest what to delay, replace or remove.

Get in Touch