Third-Party Scripts and Performance: How to Identify and Fix the Worst Offenders

Third-Party Scripts and Performance: How to Identify and Fix the Worst Offenders

BackerLeader posted Originally published at apogeewatcher.com 11 min read

Most “performance projects” start with images and fonts. Fair enough. But the pages that still feel bad after a hero image is optimised, preloaded, and served from a CDN are often suffering from a different problem: third-party JavaScript. Tag managers, chat widgets, A/B tests, and social embeds do not have to be evil. Left unmanaged, they absolutely compete with your first paint, your LCP, and the interactions that INP measures. This guide is a practical way to name the worst offenders, decide what to cut or delay, and prove the win before the marketing team files a ticket because “the numbers look wrong”.

What “third party” really means in performance work

A third-party script is any JavaScript, iframe, or stylesheet you did not write that loads from another origin: analytics, ads, reviews, personalisation, consent platforms, and the tag manager that wires them together. Browsers do not care whether a script is “small business friendly”. They still parse it, sometimes execute it in the hot path, and every long task on the main thread delays input handling and the next paint.

Core Web Vitals are the clearest public scorecard, but the mechanism underneath is the same in smaller shops and enterprise:

  • LCP can move if a late-loading script or font pushes your hero, or if third-party work delays rendering.
  • INP gets worse when the main thread is busy; third-party long tasks and heavy event listeners sit in front of your own code.
  • Total Blocking Time (TBT) in lab tools like Lighthouse measures main-thread blocking after First Contentful Paint; it often correlates with poor INP in the field, but TBT is not a substitute for INP: it can miss slow interactions that happen after load (where a lot of third-party widgets do their work).

So the job is not “remove all third parties”. The job is to order them, time them, and measure so you are not optimising in the dark.

Step 1: Get a defensible list of what actually loads

You cannot fix what you cannot see. Start with a cold, hard inventory:

  1. View source and search for and src=.** Note every external host. Do the same for link rel="preconnect" and import() chains if you use a modern bundler.
  2. Open DevTools, Network, disable cache, hard reload. Sort by “Initiator” or group by “Domain” so you see which product pulled in which chain. A single GTM or Segment container often fans out to twenty requests.
  3. Use your tag manager’s preview mode to log which tags fire on which page templates. A homepage does not have to carry the same load as a logged-in account area, but in practice one container often rules them all until someone audits it.

Chrome DevTools Network panel: request waterfall, status and type columns, and Initiator column showing chains from gtm.js and other third-party scripts

Read the Initiator column and the waterfall to see which script pulled in which chain. Use Group by options in the table header if you want rows collapsed by Domain (wording varies by Chrome version).

For clients, this inventory belongs in the same place as your design system: a short internal doc, not a one-off email. When a new pixel appears, you can ask “which tag added this?” and roll back a single change instead of re-running a mystery hunt every quarter.

Step 2: Use Lighthouse to rank third-party cost (not just bytes)

Lighthouse in Chrome (or the PageSpeed Insights “Diagnose performance issues” section) surfaces third-party impact in a dedicated audit (title: Reduce the impact of third-party code). It lists transfer size, CPU time, and main-thread blocking time by origin. Under the documented rules, Lighthouse fails this check when third-party code blocks the main thread for more than 250ms in total during the load it measures. Treat that as a first “who is expensive?” table before you dig into traces.

Depending on Lighthouse version, you may see the same idea under newer Performance insights (for example third-party coverage in Lighthouse 13’s insight model); the underlying job is unchanged: quantify cost per provider.

Pair it with:

  • “Reduce JavaScript execution time”, “Reduce the impact of third-party code” (above), and “Minimize main-thread work” where those audits appear (Lighthouse UI uses US spelling). The exact labels move between releases; the intent does not: find scripts that add long tasks and network overhead. For a walkthrough, see Identify slow third-party JavaScript.
  • A lab run on the slowest realistic conditions (mobile, throttled) so the ranking matches how your users experience the page, not a developer laptop on Wi-Fi.

Lighthouse report: Reduce the impact of third-party code, showing per-origin transfer size, main thread time, and blocking time

The same audit appears in PageSpeed Insights under performance diagnostics; use it as your first sorted table of “who costs what” before DevTools deep dives.

Lab data is not field data, but for third-party work it is often more actionable: you can block a host in DevTools, reload, and see the same page without the tag.

Step 3: Use DevTools to find the moment a script hurts

Lighthouse points at winners and losers. DevTools shows when they run:

  1. Performance panel: record a page load, then look for long yellow blocks (Scripting) and the Main flame chart. The Long Tasks API uses a 50ms threshold for a “long task”; tasks longer than that contribute to how “busy” the main thread feels. Heavy ads or frameworks often show 50–200ms+ slices.
  2. Bottom-up / Call tree on the same recording, filtered by a third-party domain, tells you if time is in parse, compile, or your own app code after a third party schedules work.
  3. Coverage tab: load a page, then record which portions of a third-party file actually run. A 400KB file that was fully downloaded but only 5% “used” is a candidate for lazy loading or removal, not a pat on the back for gzip.

Chrome DevTools Performance: Main thread track with long scripting tasks; yellow blocks show where the CPU is busy during load or interaction

Record a load (and, for INP, a real click or menu open). Long Scripting slices on the main thread are what make interactions feel delayed.

Chrome DevTools Coverage: per-file used vs unused bytes for a large third-party script, highlighting dead code to defer or remove

Coverage answers “did we even execute this?” High unused percentage means you may be shipping JS you never needed on that route.

If you are chasing INP, repeat the recording with a real interaction: open the menu, add to cart, open chat. The worst third-party work often appears after first load, when the widget initialises on scroll or on first click.

Step 4: Map findings to CWV and business impact

Not every big script is worth a fight. Sort candidates like this:

QuestionIf “yes”
Does it sit on the critical path before first meaningful paint?Push it later or to a worker pattern (see below).
Does it add long main-thread work during typical interactions?It is a prime INP target.
Is it on every page but only used on checkout?Load it on checkout routes only.
Is it duplicated (same vendor, two tags)?Merge at the source; duplicate pixels are a classic agency waste.
Does the marketing team need it for attribution this quarter?Optimise the delivery first; removal is a product decision, not a dev-only one.

In multi-site and agency work, the same “standard” GTM install often ships to every property. A template fix in the container is cheaper than renegotiating a vendor contract, so fix the shared layer before you re-architect a single site.

Step 5: Fix patterns that work in production

Defer what you can. Non-critical script tags should not sit synchronously in . Your framework or CMS may be injecting them; override at the template level where possible. For scripts you truly need early, you are trading against LCP: keep the list as short as your analytics owner allows.

Load on interaction, not on paint. Chat widgets, non-critical A/B tools, and “nice to have” personalisation can wait until scroll, a tab click, or a 2–3 second idle timeout. requestIdleCallback is not supported everywhere, but a small setTimeout after load is still a step up from “block first paint”.

Facade heavy embeds where it makes sense for your content. A static placeholder that loads the real YouTube, map, or social widget on click (or on entering the viewport) cuts initial JavaScript and often improves LCP on content-heavy pages. See Efficiently load third-party JavaScript and implement facades in a way that does not break keyboard users or your consent flow.

Move work off the main thread for eligible scripts. Partytown and similar patterns run particular scripts in a web worker. They are not a free lunch (serialisation, compatibility), but for analytics and support scripts that can cooperate, they reduce main-thread contention. Fit them into your performance budget as an experiment, not a silent default, until you have tested INP and edge cases.

Server-side and edge tagging. If your use case is “send a conversion to ad platforms”, a server GTM on Cloud Run, or API-based server events, can remove a client tag entirely. The implementation cost is higher; the client-side win is real on interaction-heavy pages.

Govern the tag manager. One owner, naming conventions, a rule that new tags do not go live in “All Pages” without review, and a quarterly cull of unused tags will save more CPU than another round of webpack tweaks.

Step 6: Prove the change and set a floor so it sticks

Re-run the same Lighthouse and DevTools profile after the change. Keep a one-page before/after note: third-party main-thread ms, LCP, INP (if you have field data in Search Console’s Core Web Vitals report or RUM), and a screenshot of the GTM version number. If someone re-imports a container without your fixes, you have an audit trail.

For portfolios, that proof is the difference between “we sped you up” and “we keep you fast when the next tag ships”. Tying the numbers to a monitoring or budget alert means a regression is an email, not a surprise in a QBR. Apogee Watcher is built for that loop: schedule PageSpeed runs, set budgets per site or template, and get alerts when lab metrics drift. We do not replace your tag manager; we give you a steady signal that the work you just shipped (or the tag someone added at 5 p.m. Friday) is still within the thresholds you and the client agreed.

If you are still building a governance story for your team, the Core Web Vitals monitoring checklist for agencies is a good companion: it includes a third-party pass as part of ongoing ops, not a one-off launch audit.

FAQ

Are third-party scripts the main cause of bad Core Web Vitals?

Not always, but they are a common cause of high INP and TBT, and they keep turning up in audits for LCP and CLS when they shift layout or delay rendering. TBT in the lab and INP in the field are related but not the same (post-load interactions matter for INP). Your own images and font strategy still matter. Treat third parties as a parallel track you measure with the same rigour as first-party code.

Should I use only field data to prioritise?

Field data (CrUX, RUM) tells you who suffers in the real world. Lab data and DevTools help you attribute that pain to a specific script before you change contracts or tags. Use both. For more on the split, read PageSpeed Insights vs automated monitoring: when manual checks are not enough and Understanding INP: the newest Core Web Vital.

What is the fastest win?

In our experience, the fastest win is cutting duplicate tags and loading chat and secondary widgets after load or on interaction, before you touch your core bundle. The slowest is migrating to full server-side tagging, which pays off for large programmes but is not a Friday afternoon change.

Will delaying analytics break reporting?

It can change “session” definitions if you delay too aggressively or if your vendor counts page views before a delayed tag fires. Align with whoever owns the data model. Often you can still send one lightweight pageview early and load heavier remarketing and enrichment after idle.

How do I explain this to a non-technical stakeholder?

Framed the right way, it is a cost conversation: every script has a time cost on the user’s device. The table from Lighthouse, plus one before/after number for LCP or INP, is usually enough to get approval for a staged rollout.

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Breaking the AI Data Bottleneck: How Hammerspace's AI Data Platform Eliminates Migration Nightmares

Tom Smithverified - Mar 16

I Wrote a Script to Fix Audible's Unreadable PDF Filenames

snapsynapseverified - Apr 20

Image Optimisation Strategies for Better LCP Scores

ApogeeWatcherverified - Apr 25

Just completed another large-scale WordPress migration — and the client left this

saqib_devmorph - Apr 7
chevron_left

Commenters (This Week)

1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!