Skip to content
Issue docs

Heavy third-party code

Standardheavy_third_party_codeIssue 189

What is this issue?

Third-party code is JavaScript the page loads from somebody else's domain -- tag managers, analytics, ads, chat widgets, social embeds. While it runs it can block the browser's main thread, so the page cannot respond to the visitor.

This check reports pages where third-party code blocked the main thread for 250 ms or more during load.

Why it matters

  • It is often the biggest single cause of poor responsiveness, feeding Total Blocking Time in the lab and Interaction to Next Paint for real users.
  • It grows quietly. Tags are added by marketing tools and rarely removed.
  • It is not always in the site owner's control, which is why this is a standard issue rather than an important one -- but how and when it loads usually is.

Fixing it removes a standard-severity finding from the site's health score.

How to fix it

  1. List what loads. The finding names the heaviest providers; check each one is still needed and still used.

  2. Load tags after the page is interactive -- defer or async, or through your tag manager's "window loaded" trigger.

  3. Replace heavy embeds with facades. Show a static thumbnail for a YouTube video or chat widget and load the real thing on click.

  4. Self-host what you can, such as fonts and small libraries, so it is not third-party code at all.

  5. Consider moving tags off the main thread with a tool such as Partytown.

Examples

1. A tag manager and a social pixel

Fails -- Google Tag Manager blocked the main thread for 310 ms and a social pixel for 110 ms: 420 ms in total.

Passes -- the pixel removed and the tag manager's tags moved to a "window loaded" trigger: 90 ms.

2. A YouTube embed

Fails -- the embed's player scripts blocked the main thread for 380 ms on a page where most visitors never press play.

Passes -- a thumbnail facade that loads the player on click.

How PixyScan detects this

  1. Chooses which pages to measure. After the crawl, PixyScan measures the homepage plus the pages your own site links to most, up to a per-scan budget (ten pages by default, at most fifty). Measuring every page would take hours and exhaust the PageSpeed Insights quota of the API key it runs under.

  2. Asks the PageSpeed Insights API about each of them, on mobile. This needs the Hobby plan or above and a PageSpeed Insights API key on the site or its workspace.

  3. Reads the Lighthouse audit third-party-summary, which groups the page's third-party code by provider and reports how long each blocked the main thread.

  4. Raises the issue when the total blocking time reaches 250 ms -- the bound Lighthouse itself fails the audit at.

  5. Newer Lighthouse versions replace that audit with third-parties-insight, which reports main-thread time but not blocking time. Main-thread time is never less than blocking time, so on its own it would over-report. With only the insight available, PixyScan raises the issue when third parties ran on the main thread for 250 ms or more and the whole page's Total Blocking Time was 250 ms or more -- both are necessary for 250 ms of third-party blocking. The finding says which measure it used.

  6. Raises nothing on a page it did not measure. A page outside the sample, and a page whose measurement failed, are both reported as Not measured. Neither is treated as passing, and neither can fail this check.

What we store

Storage Level

Page Level


Database Table / Prisma Model

PagePerformance (page_performance), with the finding itself on audit_issues.details.


Fields Used

Field Type Description
strategy String mobile or desktop. Every number in the row belongs to one form factor.
fetchedAt DateTime When PageSpeed answered. "Measured three weeks ago" is not "not measured".
errorReason String? Why a sampled page still has no numbers. Null when the call succeeded.
audits Json? A compact summary of the Lighthouse audits the opportunity checks read: per audit its source (the Lighthouse audit id it was read from), score, numericValue, displayValue, itemCount, totals over every row (wastedBytes, wastedMs, blockingTime, mainThreadTime) and the five worst rows. Keyed by the classic Lighthouse audit id. Null when the response carried no audits.

On the finding (audit_issues.details): message, plus audit (third-party-summary or third-parties-insight), measure (blockingTime or mainThreadTime), valueMs, thresholdMs (250) and items (the five heaviest providers, each named with its blockingTime or mainThreadTime).


Detection Dependencies

  • PageSpeed Insights API v5 (lighthouseResult.audits), mobile strategy
  • The PageSpeed plan feature (Hobby and above) and a PageSpeed Insights API key
  • The crawl's internal link graph, which decides which pages are sampled

Note

A page with no row was never measured. The pass samples the homepage plus the most-linked pages within a per-scan budget (default 10, at most 50), so most pages of a site have no page_performance row at all. That is the normal state, it is drawn as Not measured, and this check cannot be raised against it.

A row whose metric columns are all null with an errorReason set is a different state again: the page WAS sampled and the measurement failed. It is still never reported as passing.

Further reading