Experiments / 09

Third-Party Scripts

Who else is using your main thread?

  • Intermediate
  • 9 min
  • Impact ●●●

The problem

You can do everything in the earlier experiments right and still ship a slow page, because a large share of what runs on it isn’t yours. Analytics, tag managers, chat bubbles, ad networks, A/B tests, cookie banners and embedded video all arrive as scripts from other companies’ servers.

They are different from your own code in four ways that matter: you don’t control how big they are, you don’t control how long they take, they often load more scripts you didn’t ask for, and they come from servers you have no influence over. And they all run on the same single main thread as your page.

Add the tags one at a time

Start from a clean page, then turn tags on in the order a marketing team might: analytics, then a tag manager, then everything else. Look at who is using the main thread, and at the share that isn’t yours.

Setups
Tags on the page
Mitigations
Device CPU
10 Mbps
120 ms
Largest paint
1.98 syour page alone: 1.98 s
Third-party share
0%of main-thread work
Blocking time
931 mslongest task 993 ms
Layout shift
0.00510 KB · 4 requests · 1 origins
Who is using the main threadlighter = parsing · solid = running · red edge = long task
2.05 s

Your page on its own. Switch tags on one at a time and watch the numbers move.

Illustrative figures for typical tag types, not any real product. Tag work shares one thread with your page, one task at a time.

Things to try

  1. Add analytics alone. It looks small. Note how the numbers move anyway.
  2. Add the tag manager. It runs, then loads more scripts a moment later: look for the second burst in its row.
  3. Add A/B testing. Unlike the others, it hides the whole page until it has run. First paint moves, not just LCP.
  4. Choose “Everything” and read the “Third-party share” stat. Then switch the CPU to Desktop: the share barely changes, because both sides scale together, but the damage shrinks.
  5. Tick the mitigations one at a time and see which one moves which number. Idle loading helps the hero, facades remove the biggest cost, reserved space fixes the shift, and nothing helps the A/B snippet.
  6. Try “Everything, mitigated”. It’s much better, and it’s still not as good as your page alone. Mitigation isn’t the same as free.

What third-party code costs

Connections
Each new origin needs DNS, TCP and TLS before a single byte arrives, and you can’t reuse the connection your page already has. Eight origins from one ad network is eight handshakes.
Main-thread time
Every script is parsed and run on the same thread that handles taps and paints your page, one task at a time. A tag that arrives while your hero image is waiting to be decoded makes it wait.
Cascades
Tag managers and ad networks load other scripts, which load others. The size you can see in the HTML is the smallest part. They also make it hard to know what is on your page.
Layout shift
Banners, ads and embedded players appear after your content, and push it around unless you’ve reserved their space.
Blocking
An anti-flicker snippet deliberately keeps the page hidden. And a synchronous script from a slow or failing server can hold up everything after it: a single point of failure you don’t own.

Many of these tags are also doing something you may want: measuring, supporting customers, paying for the content. The question isn’t “remove them all”. It’s “which are worth their cost, and can we load them better?”

What helps

  • Audit. List every third-party request on a real page. Tags nobody remembers adding are common. Remove what no one uses.
  • Load without blocking. async or defer, and never a synchronous third-party script in <head>. As Experiment 03 showed, that stops it blocking the parser, not the main thread.
  • Load later. Run non-essential tags after the page is interactive or on idle, or when the visitor first interacts. Anything that can wait should.
  • Use facades. Show a lightweight placeholder for chat or video and load the real thing on click. The widget’s biggest single win.
  • Reserve space for anything that appears late: a fixed-height slot for an ad, a fixed banner area.
  • Connect early to the origins that matter with preconnect or dns-prefetch (Experiment 08), and no others.
  • Self-host scripts you can: it removes a connection and puts caching in your hands. The price is that you now own updating it, and some vendors forbid it.
  • Move work off the main thread. Some tools run third-party scripts in a Web Worker so they can’t block your page. It’s newer and not every tag works that way.
<!-- placeholder first; load the real player on click -->
<button class="video-facade" data-embed="https://player.example/embed/abc123">
  <img src="poster.jpg" width="640" height="360" alt="Play: Product tour">
</button>

Common misconceptions

“It loads async, so it’s free.”

async stops it blocking the parser. It then runs on the main thread, and anything you tap meanwhile waits for it. The timeline above has no blocking scripts and still has red long tasks.

“It’s a tiny script.”

The size of the first file is rarely the cost. Look at what it loads next. For example, a 90 KB tag manager may bring another 120 KB and a second burst of work.

“My Lighthouse score is fine, so third parties are fine.”

A lab run sees the tags that loaded in that run. Real visits get a different mix of tags, ads and consent banners. We come back to this in Experiment 12.

See it on a real page

  1. In DevTools Network, filter to third-party requests and sort by size and by initiator. Look at what a single tag causes.
  2. In the Performance panel, record a load and group the main thread by domain or by third party in the bottom-up view. That’s this widget’s “Who is using the main thread”, for real.
  3. Open Coverage and see how much of each third-party script actually ran.
  4. Lighthouse’s “Reduce the impact of third-party code” audit lists blocking time by vendor, and WebPageTest’s request map draws the cascades.

Model assumptions

What this simulation simplifies
  • The tags are generic stand-ins with plausible sizes, run times, request counts, origin counts and layout shift. They are not measurements of any real product, and the “everything” case is deliberately a worst case.
  • Your page is the critical-rendering-path model from Experiment 05, on the same device and network. Tag and page work share one thread, one task at a time, in the order they become ready.
  • Tags are loaded asynchronously. Each new origin costs three round trips, the A/B snippet’s script is requested at the same moment as your own scripts, and it hides the page until it runs or for at most four seconds.
  • Idle loading starts tags a little after the page’s largest paint. A facade replaces a tag with a 5 KB placeholder and a tiny task. Self-hosting removes one origin’s handshake (analytics only).
  • Layout shift is a fixed per-tag amount, removed by reserved space for banners, ads, chat and embeds. The A/B swap shifts regardless.