[{"data":1,"prerenderedAt":1694},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance":1685},{"id":4,"title":5,"body":6,"description":1667,"extension":1668,"meta":1669,"navigation":1180,"path":1679,"seo":1680,"stem":1683,"__hash__":1684},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Findex.md","Third-Party Script Performance",{"type":7,"value":8,"toc":1654},"minimark",[9,14,24,40,172,188,193,222,226,229,326,329,337,341,344,429,446,450,453,873,888,892,895,994,1001,1005,1008,1275,1278,1282,1285,1341,1344,1348,1357,1363,1369,1379,1383,1386,1527,1530,1533,1537,1560,1572,1581,1593,1597,1637,1641,1644,1647,1650],[10,11,13],"h1",{"id":12},"third-party-script-performance-containing-code-you-did-not-write","Third-Party Script Performance: Containing Code You Did Not Write",[15,16,17,18,23],"p",{},"This guide extends ",[19,20,22],"a",{"href":21},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting"," to the JavaScript your build never sees — the tags, widgets, embeds and consent managers that arrive at runtime from origins you do not control.",[15,25,26,27,31,32,35,36,39],{},"The asymmetry is what makes third-party code dangerous. Your own bundle is measured in CI, split by route, tree-shaken and versioned. A tag manager container is a runtime-editable script that a marketing colleague can change on a Tuesday afternoon, and it executes with the same privileges and on the same thread as your application code. It is completely normal to find a page whose first-party bundle is a well-optimised ",[28,29,30],"code",{},"140KB"," and whose third-party payload is ",[28,33,34],{},"900KB"," across eleven origins — and where every ",[28,37,38],{},"200ms"," of INP damage comes from the second number.",[41,42,49,50,57,58,57,62,57,66,57,75,57,82,57,90,57,96,57,102,57,106,57,111,57,116,57,120,57,125,57,129,57,133,57,137,57,141,57,145,57,148,57,152,57,156,57,162,57,167,57],"svg",{"xmlns":43,"viewBox":44,"width":45,"role":46,"ariaLabel":47,"style":48},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 300","100%","img","Stacked bar comparing first-party and third-party contributions to bytes, main thread time and connections, with third party dominating main thread time and connection count","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)","\n  ",[51,52],"rect",{"className":53,"x":55,"y":55,"width":45,"height":45,"fill":56},[54],"svg-canvas","0","#ffffff"," ",[59,60,61],"title",{},"First-party versus third-party cost on a typical page",[63,64,65],"desc",{},"Three horizontal bars show that third-party code contributes a majority of transferred bytes, main-thread execution time and network connections on a representative commerce page.",[51,67],{"x":68,"y":68,"width":69,"height":70,"rx":71,"fill":72,"stroke":73,"style":74},"1","758","298","10","none","currentColor","stroke-opacity:0.18",[76,77,81],"text",{"x":78,"y":79,"fill":73,"style":80},"24","34","font-size:17px;font-weight:700","Who is spending the budget",[51,83],{"x":84,"y":85,"width":86,"height":86,"rx":87,"fill":88,"style":89},"230","52","16","3","#0466c8","fill-opacity:0.55",[76,91,95],{"x":92,"y":93,"fill":73,"style":94},"254","65","font-size:11.5px","first party",[51,97],{"x":98,"y":85,"width":86,"height":86,"rx":87,"fill":99,"stroke":100,"style":101},"340","#ffc300","#b8860b","fill-opacity:0.75",[76,103,105],{"x":104,"y":93,"fill":73,"style":94},"364","third party",[76,107,110],{"x":78,"y":108,"fill":73,"style":109},"106","font-size:12px;font-weight:600","JS bytes",[51,112],{"x":113,"y":114,"width":115,"height":78,"rx":87,"fill":88,"style":89},"130","90","80",[51,117],{"x":118,"y":114,"width":119,"height":78,"rx":87,"fill":99,"stroke":100,"style":101},"210","360",[76,121,124],{"x":122,"y":123,"fill":73,"style":94},"586","107","140KB \u002F 900KB",[76,126,128],{"x":78,"y":127,"fill":73,"style":109},"156","Main thread",[51,130],{"x":113,"y":131,"width":132,"height":78,"rx":87,"fill":88,"style":89},"140","60",[51,134],{"x":135,"y":131,"width":136,"height":78,"rx":87,"fill":99,"stroke":100,"style":101},"190","380",[76,138,140],{"x":122,"y":139,"fill":73,"style":94},"157","0.4s \u002F 2.6s",[76,142,144],{"x":78,"y":143,"fill":73,"style":109},"206","Connections",[51,146],{"x":113,"y":135,"width":147,"height":78,"rx":87,"fill":88,"style":89},"40",[51,149],{"x":150,"y":135,"width":151,"height":78,"rx":87,"fill":99,"stroke":100,"style":101},"170","400",[76,153,155],{"x":122,"y":154,"fill":73,"style":94},"207","1 \u002F 11 origins",[157,158],"line",{"x1":78,"y1":159,"x2":160,"y2":159,"stroke":73,"style":161},"238","736","stroke-opacity:0.2",[76,163,166],{"x":78,"y":164,"fill":73,"style":165},"264","font-size:11.5px;fill-opacity:0.85","Optimising the first-party bar to zero would not fix this page. The lever is the second bar.",[76,168,171],{"x":78,"y":169,"fill":73,"style":170},"284","font-size:11.5px;fill-opacity:0.7","Numbers from a representative commerce route; measure your own before quoting them.",[15,173,174,175,179,180,183,184,187],{},"You usually cannot delete these scripts — they pay for the site. What you can do is decide ",[176,177,178],"em",{},"when"," they run, ",[176,181,182],{},"what thread"," they run on, and ",[176,185,186],{},"how much"," they are permitted to cost.",[189,190,192],"h2",{"id":191},"prerequisites","Prerequisites",[194,195,196,209,212,215],"ul",{},[197,198,199,200,204,205,208],"li",{},"The ",[201,202,203],"strong",{},"Coverage"," and ",[201,206,207],{},"Performance"," panels, plus the Performance panel's third-party grouping, which attributes main-thread time by origin rather than by file.",[197,210,211],{},"A list of every tag currently deployed, with an owner's name against each. Half of a typical container is inherited from campaigns nobody remembers; the fastest optimisation is deletion, and deletion needs an owner to sign off.",[197,213,214],{},"A staging environment where tags can be disabled individually. Measuring the cost of a tag by removing it is the only method that captures its true cost including the work it triggers in other scripts.",[197,216,217,218,221],{},"A ",[28,219,220],{},"Content-Security-Policy"," you can extend, since several containment techniques change the origin a script is served from.",[189,223,225],{"id":224},"_1-attribute-the-cost-per-origin","1. Attribute the Cost Per Origin",[15,227,228],{},"Aggregate numbers are useless for negotiation. You need a table that says \"this vendor costs 380ms of main-thread time on mobile\", because that is the sentence that gets a tag removed.",[230,231,236],"pre",{"className":232,"code":233,"language":234,"meta":235,"style":235},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Per-origin main-thread cost, straight out of a Lighthouse run.\nnpx lighthouse https:\u002F\u002Fexample.com --output=json --quiet --preset=desktop \\\n  | jq -r '.audits[\"third-party-summary\"].details.items[]\n           | \"\\(.mainThreadTime|round)ms  \\(.transferSize)B  \\(.entity.text \u002F\u002F .entity)\"' \\\n  | sort -rn\n# trade-off: Lighthouse attributes by known entity, so a tag proxied through your\n# own domain is credited to you. Cross-check with the Performance panel's\n# bottom-up view filtered by script URL before you defend a first-party number.\n","bash","",[28,237,238,246,274,289,297,308,314,320],{"__ignoreMap":235},[239,240,242],"span",{"class":157,"line":241},1,[239,243,245],{"class":244},"sjfSM","# Per-origin main-thread cost, straight out of a Lighthouse run.\n",[239,247,249,253,257,260,264,267,270],{"class":157,"line":248},2,[239,250,252],{"class":251},"sQw3B","npx",[239,254,256],{"class":255},"sZ8jY"," lighthouse",[239,258,259],{"class":255}," https:\u002F\u002Fexample.com",[239,261,263],{"class":262},"sPXB4"," --output=json",[239,265,266],{"class":262}," --quiet",[239,268,269],{"class":262}," --preset=desktop",[239,271,273],{"class":272},"sPARh"," \\\n",[239,275,277,280,283,286],{"class":157,"line":276},3,[239,278,279],{"class":272},"  |",[239,281,282],{"class":251}," jq",[239,284,285],{"class":262}," -r",[239,287,288],{"class":255}," '.audits[\"third-party-summary\"].details.items[]\n",[239,290,292,295],{"class":157,"line":291},4,[239,293,294],{"class":255},"           | \"\\(.mainThreadTime|round)ms  \\(.transferSize)B  \\(.entity.text \u002F\u002F .entity)\"'",[239,296,273],{"class":272},[239,298,300,302,305],{"class":157,"line":299},5,[239,301,279],{"class":272},[239,303,304],{"class":251}," sort",[239,306,307],{"class":262}," -rn\n",[239,309,311],{"class":157,"line":310},6,[239,312,313],{"class":244},"# trade-off: Lighthouse attributes by known entity, so a tag proxied through your\n",[239,315,317],{"class":157,"line":316},7,[239,318,319],{"class":244},"# own domain is credited to you. Cross-check with the Performance panel's\n",[239,321,323],{"class":157,"line":322},8,[239,324,325],{"class":244},"# bottom-up view filtered by script URL before you defend a first-party number.\n",[15,327,328],{},"Run it on the routes that matter, not the homepage. Tag deployment is rarely uniform: checkout pages routinely carry payment, fraud and analytics scripts that the landing page never loads, and that is also where interaction latency costs the most money.",[15,330,331,332,336],{},"The Coverage panel adds the second half of the picture — how much of each script's bytes were actually executed. A vendor script with 12% coverage is shipping you an entire SDK to use one function. The procedure for turning coverage output into a defensible cost table is in ",[19,333,335],{"href":334},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Fmeasuring-third-party-cost-with-the-coverage-panel\u002F","measuring third-party cost with the Coverage panel",".",[189,338,340],{"id":339},"_2-fix-the-loading-attributes-first","2. Fix the Loading Attributes First",[15,342,343],{},"Before any architectural work, correct the trivially wrong cases. A surprising share of third-party damage comes from scripts loaded with the wrong attribute, and the fix is a one-line change.",[345,346,347,363],"table",{},[348,349,350],"thead",{},[351,352,353,357,360],"tr",{},[354,355,356],"th",{},"Pattern",[354,358,359],{},"Effect",[354,361,362],{},"Use for",[364,365,366,381,394,407,418],"tbody",{},[351,367,368,375,378],{},[369,370,371,374],"td",{},[28,372,373],{},"\u003Cscript src>"," (no attribute)",[369,376,377],{},"Blocks the parser until fetched and executed",[369,379,380],{},"Nothing on a modern page",[351,382,383,388,391],{},[369,384,385],{},[28,386,387],{},"\u003Cscript async>",[369,389,390],{},"Downloads in parallel, executes as soon as it arrives — can interrupt anything",[369,392,393],{},"Independent tags with no ordering needs",[351,395,396,401,404],{},[369,397,398],{},[28,399,400],{},"\u003Cscript defer>",[369,402,403],{},"Downloads in parallel, executes in order after parsing",[369,405,406],{},"Scripts that touch the DOM",[351,408,409,412,415],{},[369,410,411],{},"Injected on interaction",[369,413,414],{},"No cost until the user opts in",[369,416,417],{},"Chat, video, maps, social embeds",[351,419,420,423,426],{},[369,421,422],{},"Worker-isolated",[369,424,425],{},"Runs off the main thread entirely",[369,427,428],{},"Analytics and tag containers",[15,430,431,434,435,437,438,441,442,445],{},[28,432,433],{},"async"," is the default recommendation in most vendor documentation and it is often the wrong one: an ",[28,436,433],{}," script can execute at any moment, including in the middle of hydration or during a user interaction, which is precisely how a tag manager lands a ",[28,439,440],{},"340ms"," task on top of a click. ",[28,443,444],{},"defer"," at least guarantees execution after parsing, in a predictable order.",[189,447,449],{"id":448},"_3-replace-heavy-embeds-with-facades","3. Replace Heavy Embeds with Facades",[15,451,452],{},"The largest single win available on most content pages is the facade: render a lightweight, accurate-looking placeholder, and load the real embed only when the user interacts with it.",[230,454,458],{"className":455,"code":456,"language":457,"meta":235,"style":235},"language-html shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u003C!-- A video facade: poster image + play affordance, no vendor JS until clicked. -->\n\u003Cbutton class=\"video-facade\" data-video=\"dQw4w9WgXcQ\"\n        aria-label=\"Play: Building a performance budget\">\n  \u003Cimg src=\"\u002Fthumbs\u002Fperf-budget.avif\" width=\"1280\" height=\"720\" alt=\"\"\n       loading=\"lazy\" decoding=\"async\">\n  \u003Cspan class=\"play-icon\" aria-hidden=\"true\">\u003C\u002Fspan>\n\u003C\u002Fbutton>\n\u003Cscript type=\"module\">\n  document.querySelectorAll('.video-facade').forEach((el) => {\n    el.addEventListener('click', () => {\n      const f = document.createElement('iframe');\n      f.src = `https:\u002F\u002Fwww.youtube-nocookie.com\u002Fembed\u002F${el.dataset.video}?autoplay=1`;\n      f.allow = 'accelerometer; autoplay; encrypted-media; picture-in-picture';\n      f.title = el.getAttribute('aria-label').replace(\u002F^Play: \u002F, '');\n      f.width = 1280; f.height = 720; f.loading = 'lazy';\n      el.replaceWith(f);\n    }, { once: true });\n  });\n\u003C\u002Fscript>\n\u003C!-- trade-off: the facade costs one extra click before playback and forfeits\n     vendor analytics on impressions. For a video that 4% of visitors play, that\n     is an excellent deal; for an autoplaying hero video it is not applicable. -->\n","html",[28,459,460,465,492,505,544,564,592,601,618,655,676,702,731,744,787,816,828,840,846,855,861,867],{"__ignoreMap":235},[239,461,462],{"class":157,"line":241},[239,463,464],{"class":244},"\u003C!-- A video facade: poster image + play affordance, no vendor JS until clicked. -->\n",[239,466,467,471,475,478,481,484,487,489],{"class":157,"line":248},[239,468,470],{"class":469},"saISM","\u003C",[239,472,474],{"class":473},"sZBmE","button",[239,476,477],{"class":262}," class",[239,479,480],{"class":469},"=",[239,482,483],{"class":255},"\"video-facade\"",[239,485,486],{"class":262}," data-video",[239,488,480],{"class":469},[239,490,491],{"class":255},"\"dQw4w9WgXcQ\"\n",[239,493,494,497,499,502],{"class":157,"line":276},[239,495,496],{"class":262},"        aria-label",[239,498,480],{"class":469},[239,500,501],{"class":255},"\"Play: Building a performance budget\"",[239,503,504],{"class":469},">\n",[239,506,507,510,512,515,517,520,523,525,528,531,533,536,539,541],{"class":157,"line":291},[239,508,509],{"class":469},"  \u003C",[239,511,46],{"class":473},[239,513,514],{"class":262}," src",[239,516,480],{"class":469},[239,518,519],{"class":255},"\"\u002Fthumbs\u002Fperf-budget.avif\"",[239,521,522],{"class":262}," width",[239,524,480],{"class":469},[239,526,527],{"class":255},"\"1280\"",[239,529,530],{"class":262}," height",[239,532,480],{"class":469},[239,534,535],{"class":255},"\"720\"",[239,537,538],{"class":262}," alt",[239,540,480],{"class":469},[239,542,543],{"class":255},"\"\"\n",[239,545,546,549,551,554,557,559,562],{"class":157,"line":299},[239,547,548],{"class":262},"       loading",[239,550,480],{"class":469},[239,552,553],{"class":255},"\"lazy\"",[239,555,556],{"class":262}," decoding",[239,558,480],{"class":469},[239,560,561],{"class":255},"\"async\"",[239,563,504],{"class":469},[239,565,566,568,570,572,574,577,580,582,585,588,590],{"class":157,"line":310},[239,567,509],{"class":469},[239,569,239],{"class":473},[239,571,477],{"class":262},[239,573,480],{"class":469},[239,575,576],{"class":255},"\"play-icon\"",[239,578,579],{"class":262}," aria-hidden",[239,581,480],{"class":469},[239,583,584],{"class":255},"\"true\"",[239,586,587],{"class":469},">\u003C\u002F",[239,589,239],{"class":473},[239,591,504],{"class":469},[239,593,594,597,599],{"class":157,"line":316},[239,595,596],{"class":469},"\u003C\u002F",[239,598,474],{"class":473},[239,600,504],{"class":469},[239,602,603,605,608,611,613,616],{"class":157,"line":322},[239,604,470],{"class":469},[239,606,607],{"class":473},"script",[239,609,610],{"class":262}," type",[239,612,480],{"class":469},[239,614,615],{"class":255},"\"module\"",[239,617,504],{"class":469},[239,619,621,624,628,631,634,637,640,643,646,649,652],{"class":157,"line":620},9,[239,622,623],{"class":469},"  document.",[239,625,627],{"class":626},"smZ65","querySelectorAll",[239,629,630],{"class":469},"(",[239,632,633],{"class":255},"'.video-facade'",[239,635,636],{"class":469},").",[239,638,639],{"class":626},"forEach",[239,641,642],{"class":469},"((",[239,644,645],{"class":251},"el",[239,647,648],{"class":469},") ",[239,650,651],{"class":272},"=>",[239,653,654],{"class":469}," {\n",[239,656,658,661,664,666,669,672,674],{"class":157,"line":657},10,[239,659,660],{"class":469},"    el.",[239,662,663],{"class":626},"addEventListener",[239,665,630],{"class":469},[239,667,668],{"class":255},"'click'",[239,670,671],{"class":469},", () ",[239,673,651],{"class":272},[239,675,654],{"class":469},[239,677,679,682,685,688,691,694,696,699],{"class":157,"line":678},11,[239,680,681],{"class":272},"      const",[239,683,684],{"class":262}," f",[239,686,687],{"class":272}," =",[239,689,690],{"class":469}," document.",[239,692,693],{"class":626},"createElement",[239,695,630],{"class":469},[239,697,698],{"class":255},"'iframe'",[239,700,701],{"class":469},");\n",[239,703,705,708,710,713,715,717,720,722,725,728],{"class":157,"line":704},12,[239,706,707],{"class":469},"      f.src ",[239,709,480],{"class":272},[239,711,712],{"class":255}," `https:\u002F\u002Fwww.youtube-nocookie.com\u002Fembed\u002F${",[239,714,645],{"class":469},[239,716,336],{"class":255},[239,718,719],{"class":469},"dataset",[239,721,336],{"class":255},[239,723,724],{"class":469},"video",[239,726,727],{"class":255},"}?autoplay=1`",[239,729,730],{"class":469},";\n",[239,732,734,737,739,742],{"class":157,"line":733},13,[239,735,736],{"class":469},"      f.allow ",[239,738,480],{"class":272},[239,740,741],{"class":255}," 'accelerometer; autoplay; encrypted-media; picture-in-picture'",[239,743,730],{"class":469},[239,745,747,750,752,755,758,760,763,765,768,770,773,776,779,782,785],{"class":157,"line":746},14,[239,748,749],{"class":469},"      f.title ",[239,751,480],{"class":272},[239,753,754],{"class":469}," el.",[239,756,757],{"class":626},"getAttribute",[239,759,630],{"class":469},[239,761,762],{"class":255},"'aria-label'",[239,764,636],{"class":469},[239,766,767],{"class":626},"replace",[239,769,630],{"class":469},[239,771,772],{"class":255},"\u002F",[239,774,775],{"class":272},"^",[239,777,778],{"class":255},"Play: \u002F",[239,780,781],{"class":469},", ",[239,783,784],{"class":255},"''",[239,786,701],{"class":469},[239,788,790,793,795,798,801,803,806,809,811,814],{"class":157,"line":789},15,[239,791,792],{"class":469},"      f.width ",[239,794,480],{"class":272},[239,796,797],{"class":262}," 1280",[239,799,800],{"class":469},"; f.height ",[239,802,480],{"class":272},[239,804,805],{"class":262}," 720",[239,807,808],{"class":469},"; f.loading ",[239,810,480],{"class":272},[239,812,813],{"class":255}," 'lazy'",[239,815,730],{"class":469},[239,817,819,822,825],{"class":157,"line":818},16,[239,820,821],{"class":469},"      el.",[239,823,824],{"class":626},"replaceWith",[239,826,827],{"class":469},"(f);\n",[239,829,831,834,837],{"class":157,"line":830},17,[239,832,833],{"class":469},"    }, { once: ",[239,835,836],{"class":262},"true",[239,838,839],{"class":469}," });\n",[239,841,843],{"class":157,"line":842},18,[239,844,845],{"class":469},"  });\n",[239,847,849,851,853],{"class":157,"line":848},19,[239,850,596],{"class":469},[239,852,607],{"class":473},[239,854,504],{"class":469},[239,856,858],{"class":157,"line":857},20,[239,859,860],{"class":244},"\u003C!-- trade-off: the facade costs one extra click before playback and forfeits\n",[239,862,864],{"class":157,"line":863},21,[239,865,866],{"class":244},"     vendor analytics on impressions. For a video that 4% of visitors play, that\n",[239,868,870],{"class":157,"line":869},22,[239,871,872],{"class":244},"     is an excellent deal; for an autoplaying hero video it is not applicable. -->\n",[15,874,875,876,879,880,883,884,336],{},"A single YouTube embed pulls roughly ",[28,877,878],{},"500–900KB"," and several hundred milliseconds of main-thread time on a mobile profile; the facade above costs a ",[28,881,882],{},"30KB"," image. The same pattern applies to maps, chat widgets, comment systems and social embeds. The complete implementation, including keyboard accessibility and preserving the aspect ratio so the swap causes no layout shift, is in ",[19,885,887],{"href":886},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Freplacing-youtube-embeds-with-a-facade\u002F","replacing YouTube embeds with a facade",[189,889,891],{"id":890},"_4-move-containers-off-the-main-thread","4. Move Containers Off the Main Thread",[15,893,894],{},"For tags that must run on every page view — analytics, consent, experimentation — the remaining lever is the thread they run on. A worker-based sandbox executes vendor JavaScript in a Web Worker and proxies its DOM access back to the main thread asynchronously.",[41,896,49,899,57,902,57,905,57,908,57,911,57,914,57,921,57,927,57,933,57,938,57,941,57,946,57,950,57,955,57,958,57,961,57,964,57,966,57,969,57,974,57,977,57,982,57,988,57,991,57],{"xmlns":43,"viewBox":897,"width":45,"role":46,"ariaLabel":898,"style":48},"0 0 760 276","Architecture diagram contrasting a tag container executing directly on the main thread with the same container executing inside a web worker that proxies DOM access back asynchronously",[51,900],{"className":901,"x":55,"y":55,"width":45,"height":45,"fill":56},[54],[59,903,904],{},"Tag container on the main thread versus in a worker sandbox",[63,906,907],{},"On the left the container shares the main thread with rendering and event handling; on the right it runs in a worker and communicates with a main-thread proxy that applies DOM operations between frames.",[51,909],{"x":68,"y":68,"width":69,"height":910,"rx":71,"fill":72,"stroke":73,"style":74},"274",[76,912,913],{"x":78,"y":79,"fill":73,"style":80},"Two places to run a tag container",[51,915],{"x":916,"y":917,"width":918,"height":135,"rx":919,"fill":73,"stroke":73,"style":920},"28","56","330","8","fill-opacity:0.03;stroke-opacity:0.28",[76,922,926],{"x":923,"y":115,"fill":73,"style":924,"textAnchor":925},"193","font-size:12.5px;font-weight:700;","middle","Direct",[51,928],{"x":85,"y":929,"width":930,"height":79,"rx":931,"fill":88,"stroke":88,"style":932},"96","282","5","fill-opacity:0.14;stroke-opacity:0.5",[76,934,937],{"x":923,"y":935,"fill":73,"style":936,"textAnchor":925},"118","font-size:11.5px;","main thread: render + events",[51,939],{"x":85,"y":131,"width":930,"height":79,"rx":931,"fill":99,"stroke":100,"style":940},"fill-opacity:0.28",[76,942,945],{"x":923,"y":943,"fill":73,"style":944,"textAnchor":925},"162","font-size:11.5px;font-weight:700;","tag container executes here too",[76,947,949],{"x":923,"y":948,"fill":73,"style":936,"textAnchor":925},"200","one queue, vendor code first in line",[76,951,954],{"x":923,"y":952,"fill":73,"style":953,"textAnchor":925},"222","font-size:11.5px;fill-opacity:0.75","every tag task delays every interaction",[51,956],{"x":957,"y":917,"width":918,"height":135,"rx":919,"fill":73,"stroke":73,"style":920},"402",[76,959,422],{"x":960,"y":115,"fill":73,"style":924,"textAnchor":925},"567",[51,962],{"x":963,"y":929,"width":930,"height":79,"rx":931,"fill":88,"stroke":88,"style":932},"426",[76,965,937],{"x":960,"y":935,"fill":73,"style":936,"textAnchor":925},[51,967],{"x":963,"y":131,"width":113,"height":79,"rx":931,"fill":73,"stroke":73,"style":968},"fill-opacity:0.08;stroke-opacity:0.35",[76,970,973],{"x":971,"y":943,"fill":73,"style":972,"textAnchor":925},"491","font-size:11px;","DOM proxy",[51,975],{"x":976,"y":131,"width":113,"height":79,"rx":931,"fill":99,"stroke":100,"style":940},"578",[76,978,981],{"x":979,"y":943,"fill":73,"style":980,"textAnchor":925},"643","font-size:11px;font-weight:700;","worker: container",[157,983],{"x1":984,"y1":139,"x2":985,"y2":139,"stroke":73,"strokeDashArray":986,"style":987},"556","572",[87,87],"stroke-opacity:0.55",[76,989,990],{"x":960,"y":948,"fill":73,"style":936,"textAnchor":925},"tag work never blocks a click",[76,992,993],{"x":960,"y":952,"fill":73,"style":953,"textAnchor":925},"cost: proxy latency, some APIs unavailable",[15,995,996,997,336],{},"Worker isolation is powerful and genuinely lossy: synchronous DOM reads become asynchronous, some vendor scripts detect the sandbox and misbehave, and debugging moves into a second execution context. It suits analytics and measurement tags — which mostly write, rarely read, and tolerate a few milliseconds of latency — far better than scripts that manipulate visible UI. The comparison against simply deferring the same container is worked through in ",[19,998,1000],{"href":999},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Fpartytown-vs-async-script-loading-for-tag-managers\u002F","Partytown vs async script loading for tag managers",[189,1002,1004],{"id":1003},"_5-gate-everything-else-behind-intent","5. Gate Everything Else Behind Intent",[15,1006,1007],{},"For the remaining tags that are neither critical nor worker-safe, the cheapest strategy is to load them when the user shows intent, or when the thread is genuinely idle — whichever comes first.",[230,1009,1013],{"className":1010,"code":1011,"language":1012,"meta":235,"style":235},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Load non-critical tags after first interaction, with an idle-time fallback.\nconst loadTags = (() => {\n  let done = false;\n  return () => {\n    if (done) return; done = true;\n    for (const src of ['\u002Ftags\u002Fheatmap.js', '\u002Ftags\u002Fsurvey.js']) {\n      const s = document.createElement('script');\n      s.src = src; s.defer = true;\n      document.head.append(s);\n    }\n  };\n})();\n\n['pointerdown', 'keydown', 'scroll'].forEach((t) =>\n  addEventListener(t, loadTags, { once: true, passive: true }));\nif ('requestIdleCallback' in window) requestIdleCallback(loadTags, { timeout: 8000 });\n\u002F\u002F trade-off: interaction-gated tags miss bounce visits entirely, which changes\n\u002F\u002F what your analytics measures. Agree the shift with the data owner before\n\u002F\u002F shipping it, or you will be asked to revert it after the first weekly report.\n","javascript",[28,1014,1015,1020,1037,1052,1064,1085,1114,1134,1150,1161,1166,1171,1176,1182,1215,1233,1260,1265,1270],{"__ignoreMap":235},[239,1016,1017],{"class":157,"line":241},[239,1018,1019],{"class":244},"\u002F\u002F Load non-critical tags after first interaction, with an idle-time fallback.\n",[239,1021,1022,1025,1028,1030,1033,1035],{"class":157,"line":248},[239,1023,1024],{"class":272},"const",[239,1026,1027],{"class":262}," loadTags",[239,1029,687],{"class":272},[239,1031,1032],{"class":469}," (() ",[239,1034,651],{"class":272},[239,1036,654],{"class":469},[239,1038,1039,1042,1045,1047,1050],{"class":157,"line":276},[239,1040,1041],{"class":272},"  let",[239,1043,1044],{"class":469}," done ",[239,1046,480],{"class":272},[239,1048,1049],{"class":262}," false",[239,1051,730],{"class":469},[239,1053,1054,1057,1060,1062],{"class":157,"line":291},[239,1055,1056],{"class":272},"  return",[239,1058,1059],{"class":469}," () ",[239,1061,651],{"class":272},[239,1063,654],{"class":469},[239,1065,1066,1069,1072,1075,1078,1080,1083],{"class":157,"line":299},[239,1067,1068],{"class":272},"    if",[239,1070,1071],{"class":469}," (done) ",[239,1073,1074],{"class":272},"return",[239,1076,1077],{"class":469},"; done ",[239,1079,480],{"class":272},[239,1081,1082],{"class":262}," true",[239,1084,730],{"class":469},[239,1086,1087,1090,1093,1095,1097,1100,1103,1106,1108,1111],{"class":157,"line":310},[239,1088,1089],{"class":272},"    for",[239,1091,1092],{"class":469}," (",[239,1094,1024],{"class":272},[239,1096,514],{"class":262},[239,1098,1099],{"class":272}," of",[239,1101,1102],{"class":469}," [",[239,1104,1105],{"class":255},"'\u002Ftags\u002Fheatmap.js'",[239,1107,781],{"class":469},[239,1109,1110],{"class":255},"'\u002Ftags\u002Fsurvey.js'",[239,1112,1113],{"class":469},"]) {\n",[239,1115,1116,1118,1121,1123,1125,1127,1129,1132],{"class":157,"line":316},[239,1117,681],{"class":272},[239,1119,1120],{"class":262}," s",[239,1122,687],{"class":272},[239,1124,690],{"class":469},[239,1126,693],{"class":626},[239,1128,630],{"class":469},[239,1130,1131],{"class":255},"'script'",[239,1133,701],{"class":469},[239,1135,1136,1139,1141,1144,1146,1148],{"class":157,"line":322},[239,1137,1138],{"class":469},"      s.src ",[239,1140,480],{"class":272},[239,1142,1143],{"class":469}," src; s.defer ",[239,1145,480],{"class":272},[239,1147,1082],{"class":262},[239,1149,730],{"class":469},[239,1151,1152,1155,1158],{"class":157,"line":620},[239,1153,1154],{"class":469},"      document.head.",[239,1156,1157],{"class":626},"append",[239,1159,1160],{"class":469},"(s);\n",[239,1162,1163],{"class":157,"line":657},[239,1164,1165],{"class":469},"    }\n",[239,1167,1168],{"class":157,"line":678},[239,1169,1170],{"class":469},"  };\n",[239,1172,1173],{"class":157,"line":704},[239,1174,1175],{"class":469},"})();\n",[239,1177,1178],{"class":157,"line":733},[239,1179,1181],{"emptyLinePlaceholder":1180},true,"\n",[239,1183,1184,1187,1190,1192,1195,1197,1200,1203,1205,1207,1210,1212],{"class":157,"line":746},[239,1185,1186],{"class":469},"[",[239,1188,1189],{"class":255},"'pointerdown'",[239,1191,781],{"class":469},[239,1193,1194],{"class":255},"'keydown'",[239,1196,781],{"class":469},[239,1198,1199],{"class":255},"'scroll'",[239,1201,1202],{"class":469},"].",[239,1204,639],{"class":626},[239,1206,642],{"class":469},[239,1208,1209],{"class":251},"t",[239,1211,648],{"class":469},[239,1213,1214],{"class":272},"=>\n",[239,1216,1217,1220,1223,1225,1228,1230],{"class":157,"line":789},[239,1218,1219],{"class":626},"  addEventListener",[239,1221,1222],{"class":469},"(t, loadTags, { once: ",[239,1224,836],{"class":262},[239,1226,1227],{"class":469},", passive: ",[239,1229,836],{"class":262},[239,1231,1232],{"class":469}," }));\n",[239,1234,1235,1238,1240,1243,1246,1249,1252,1255,1258],{"class":157,"line":818},[239,1236,1237],{"class":272},"if",[239,1239,1092],{"class":469},[239,1241,1242],{"class":255},"'requestIdleCallback'",[239,1244,1245],{"class":272}," in",[239,1247,1248],{"class":469}," window) ",[239,1250,1251],{"class":626},"requestIdleCallback",[239,1253,1254],{"class":469},"(loadTags, { timeout: ",[239,1256,1257],{"class":262},"8000",[239,1259,839],{"class":469},[239,1261,1262],{"class":157,"line":830},[239,1263,1264],{"class":244},"\u002F\u002F trade-off: interaction-gated tags miss bounce visits entirely, which changes\n",[239,1266,1267],{"class":157,"line":842},[239,1268,1269],{"class":244},"\u002F\u002F what your analytics measures. Agree the shift with the data owner before\n",[239,1271,1272],{"class":157,"line":848},[239,1273,1274],{"class":244},"\u002F\u002F shipping it, or you will be asked to revert it after the first weekly report.\n",[15,1276,1277],{},"That last trade-off is the real obstacle to third-party work, and it is organisational rather than technical. Bring the data owner the per-origin cost table from step 1 and a proposal for what is measured instead; a change that arrives as a surprise gets reverted.",[189,1279,1281],{"id":1280},"deconstructing-a-tags-true-cost","Deconstructing a Tag's True Cost",[15,1283,1284],{},"A vendor's own byte count understates its cost by a wide margin. Count all five components.",[194,1286,1287,1301,1307,1317,1323],{},[197,1288,1289,1292,1293,1296,1297,1300],{},[201,1290,1291],{},"Connection setup"," — DNS, TCP and TLS to a new origin costs ",[28,1294,1295],{},"100–300ms"," before a single byte of script arrives. Eleven origins is eleven handshakes, and ",[28,1298,1299],{},"preconnect"," only helps the two or three you hint.",[197,1302,1303,1306],{},[201,1304,1305],{},"Transfer"," — the reported size, usually the smallest term.",[197,1308,1309,1312,1313,1316],{},[201,1310,1311],{},"Parse and compile"," — roughly ",[28,1314,1315],{},"1ms"," per KB of JavaScript on a mid-tier mobile CPU, paid whether or not the code is used.",[197,1318,1319,1322],{},[201,1320,1321],{},"Execution"," — including the work the tag schedules later: timers, mutation observers, and its own secondary script loads. A container that loads six more scripts is six more of every line above.",[197,1324,1325,1328,1329,1332,1333,1336,1337,1340],{},[201,1326,1327],{},"Ongoing interference"," — long-lived observers, ",[28,1330,1331],{},"setInterval"," polling, and event listeners on ",[28,1334,1335],{},"document"," that run inside ",[176,1338,1339],{},"your"," interaction handlers and land directly in INP.",[15,1342,1343],{},"The last term is invisible in a load-time audit and is the most common reason a \"small\" tag hurts interaction latency. Look for it in the Performance panel by recording an interaction, not a load, and reading the bottom-up view for scripts you did not write.",[189,1345,1347],{"id":1346},"advanced-diagnostics","Advanced Diagnostics",[15,1349,1350,1353,1354,1356],{},[201,1351,1352],{},"Attribute INP to third-party listeners."," Field attribution gives you the interaction target; the Performance panel tells you which listener on that target was slow. When the answer is a vendor's delegated ",[28,1355,1335],{}," listener, no amount of first-party optimisation will help — the fix is to remove, defer or sandbox that tag.",[15,1358,1359,1362],{},[201,1360,1361],{},"Watch for tag-triggered layout thrash."," Heatmap and session-replay tools frequently read layout properties in a loop over the whole document. That forces synchronous reflow, which shows up as CLS-adjacent jank and slow scrolling. Detect it by profiling a scroll with the tag enabled and disabled.",[15,1364,1365,1368],{},[201,1366,1367],{},"Self-hosting a vendor script trades one risk for another."," Proxying the file through your own origin removes a connection and lets you cache it properly, but you now own its update cadence and its security review. It is the right call for a stable, versioned SDK and the wrong one for a script the vendor patches weekly.",[15,1370,1371,1374,1375,336],{},[201,1372,1373],{},"Consent managers are on the critical path by design."," They render-block by necessity, and they are frequently the single largest blocking script on the page. Push for a build that is inlined, minimal and free of framework dependencies — and confirm whether it can be served from your own origin as described in ",[19,1376,1378],{"href":1377},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002F","CDN edge caching configuration",[189,1380,1382],{"id":1381},"validation-and-budgeting","Validation and Budgeting",[15,1384,1385],{},"Third-party budgets have to be enforced automatically, because the payload changes without a deploy.",[230,1387,1391],{"className":1388,"code":1389,"language":1390,"meta":235,"style":235},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","{\n  \"ci\": {\n    \"assert\": {\n      \"assertions\": {\n        \"third-party-summary\": [\"error\", { \"maxNumericValue\": 250 }],\n        \"total-byte-weight\": [\"error\", { \"maxNumericValue\": 1600000 }],\n        \"bootup-time\": [\"error\", { \"maxNumericValue\": 1500 }],\n        \"uses-rel-preconnect\": [\"warn\", { \"minScore\": 1 }]\n      }\n    }\n  }\n}\n","json",[28,1392,1393,1398,1406,1413,1420,1446,1466,1486,1508,1513,1517,1522],{"__ignoreMap":235},[239,1394,1395],{"class":157,"line":241},[239,1396,1397],{"class":469},"{\n",[239,1399,1400,1403],{"class":157,"line":248},[239,1401,1402],{"class":473},"  \"ci\"",[239,1404,1405],{"class":469},": {\n",[239,1407,1408,1411],{"class":157,"line":276},[239,1409,1410],{"class":473},"    \"assert\"",[239,1412,1405],{"class":469},[239,1414,1415,1418],{"class":157,"line":291},[239,1416,1417],{"class":473},"      \"assertions\"",[239,1419,1405],{"class":469},[239,1421,1422,1425,1428,1431,1434,1437,1440,1443],{"class":157,"line":299},[239,1423,1424],{"class":473},"        \"third-party-summary\"",[239,1426,1427],{"class":469},": [",[239,1429,1430],{"class":255},"\"error\"",[239,1432,1433],{"class":469},", { ",[239,1435,1436],{"class":473},"\"maxNumericValue\"",[239,1438,1439],{"class":469},": ",[239,1441,1442],{"class":262},"250",[239,1444,1445],{"class":469}," }],\n",[239,1447,1448,1451,1453,1455,1457,1459,1461,1464],{"class":157,"line":310},[239,1449,1450],{"class":473},"        \"total-byte-weight\"",[239,1452,1427],{"class":469},[239,1454,1430],{"class":255},[239,1456,1433],{"class":469},[239,1458,1436],{"class":473},[239,1460,1439],{"class":469},[239,1462,1463],{"class":262},"1600000",[239,1465,1445],{"class":469},[239,1467,1468,1471,1473,1475,1477,1479,1481,1484],{"class":157,"line":316},[239,1469,1470],{"class":473},"        \"bootup-time\"",[239,1472,1427],{"class":469},[239,1474,1430],{"class":255},[239,1476,1433],{"class":469},[239,1478,1436],{"class":473},[239,1480,1439],{"class":469},[239,1482,1483],{"class":262},"1500",[239,1485,1445],{"class":469},[239,1487,1488,1491,1493,1496,1498,1501,1503,1505],{"class":157,"line":322},[239,1489,1490],{"class":473},"        \"uses-rel-preconnect\"",[239,1492,1427],{"class":469},[239,1494,1495],{"class":255},"\"warn\"",[239,1497,1433],{"class":469},[239,1499,1500],{"class":473},"\"minScore\"",[239,1502,1439],{"class":469},[239,1504,68],{"class":262},[239,1506,1507],{"class":469}," }]\n",[239,1509,1510],{"class":157,"line":620},[239,1511,1512],{"class":469},"      }\n",[239,1514,1515],{"class":157,"line":657},[239,1516,1165],{"class":469},[239,1518,1519],{"class":157,"line":678},[239,1520,1521],{"class":469},"  }\n",[239,1523,1524],{"class":157,"line":704},[239,1525,1526],{"class":469},"}\n",[15,1528,1529],{},"Run this on a schedule against production, not only on pull requests. A tag added through a container never touches your repository, so a PR-only gate cannot see it — a nightly run against the live site is what catches the Tuesday-afternoon change.",[15,1531,1532],{},"Pair the lab budget with a field alert on INP segmented by whether the visit loaded the tag in question. When a new tag ships, that segmentation answers the question \"did it hurt?\" within a day, and it answers it with the population that matters.",[189,1534,1536],{"id":1535},"faq","FAQ",[1538,1539,1542,1546],"details",{"className":1540},[1541],"faq-item",[1543,1544,1545],"summary",{},"Is async or defer better for a third-party tag?",[15,1547,1548,1550,1551,1553,1554,1556,1557,1559],{},[28,1549,444],{}," is the safer default. Both download in parallel with parsing, but ",[28,1552,433],{}," executes the moment the file arrives, which can be in the middle of hydration or a user interaction, producing an unpredictable long task exactly when it hurts most. ",[28,1555,444],{}," executes after parsing, in document order, so the timing is predictable and the script cannot interrupt earlier work. Reserve ",[28,1558,433],{}," for tags that must run as early as possible and have no ordering requirements.",[1538,1561,1563,1566],{"className":1562},[1541],[1543,1564,1565],{},"Does self-hosting a vendor script make it faster?",[15,1567,1568,1569,1571],{},"It removes the connection setup and lets you apply your own caching, which is usually worth ",[28,1570,1295],{}," on a cold visit. It does not reduce parse, compile or execution cost, which is where most of the damage is. Self-host stable, versioned SDKs; leave frequently patched scripts with the vendor so you are not shipping stale code, and remember that you also inherit the security review when you host it.",[1538,1573,1575,1578],{"className":1574},[1541],[1543,1576,1577],{},"Will a worker sandbox break my analytics?",[15,1579,1580],{},"It breaks anything that reads the DOM synchronously or depends on APIs the worker context lacks. Measurement tags that record page views, clicks and custom events generally work; scripts that render UI, read layout, or interrogate the DOM in tight loops generally do not. Validate event-for-event against a control before rolling out, because a silent 5% event loss is far more expensive than the milliseconds you saved.",[1538,1582,1584,1587],{"className":1583},[1541],[1543,1585,1586],{},"How do I prove a specific tag is responsible for an INP regression?",[15,1588,1589,1590,1592],{},"Record an interaction — not a page load — in the Performance panel with the tag enabled, then disable it and record the same interaction. Compare the bottom-up view for the input handler: a vendor's delegated listener on ",[28,1591,1335],{}," will show up in the first trace and vanish in the second. Confirm in the field by segmenting INP on visits that did and did not receive the tag.",[189,1594,1596],{"id":1595},"related","Related",[194,1598,1599,1606,1613,1619,1624,1630],{},[197,1600,1601,1605],{},[19,1602,1604],{"href":1603},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Fdeferring-non-critical-analytics-scripts-safely\u002F","Deferring non-critical analytics scripts safely"," — the delivery mechanics of moving a tag off the critical path.",[197,1607,1608,1612],{},[19,1609,1611],{"href":1610},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002Freducing-input-delay-from-third-party-tags\u002F","Reducing input delay from third-party tags"," — the INP-side diagnosis when someone else's listener is in your handler.",[197,1614,1615,1618],{},[19,1616,1617],{"href":886},"Replacing YouTube embeds with a facade"," — the highest-yield single change on most content pages.",[197,1620,1621,1623],{},[19,1622,1000],{"href":999}," — when worker isolation beats simply deferring.",[197,1625,1626,1629],{},[19,1627,1628],{"href":334},"Measuring third-party cost with the Coverage panel"," — build the per-vendor cost table that wins the argument.",[197,1631,1632,1636],{},[19,1633,1635],{"href":1634},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002F","Hydration & Partial Rehydration Strategies"," — protect the main thread you just freed from your own framework.",[607,1638,1640],{"type":1639},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Contain third-party script cost\",\n  \"description\": \"Attribute main-thread cost per third-party origin, correct script loading attributes, replace heavy embeds with facades, isolate containers in a worker, and enforce a third-party budget in CI.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Attribute cost per origin\", \"text\": \"Produce a table of main-thread time and transferred bytes per third-party entity on the routes that matter.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F#1-attribute-the-cost-per-origin\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Correct loading attributes\", \"text\": \"Replace parser-blocking and unnecessarily async tags with defer or interaction-gated injection.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F#2-fix-the-loading-attributes-first\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Apply facades to embeds\", \"text\": \"Render a static placeholder for video, map and chat embeds and load the vendor iframe only on interaction.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F#3-replace-heavy-embeds-with-facades\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Isolate containers in a worker\", \"text\": \"Run measurement tags in a worker sandbox that proxies DOM access asynchronously so they cannot block interactions.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F#4-move-containers-off-the-main-thread\" },\n    { \"@type\": \"HowToStep\", \"position\": 5, \"name\": \"Gate the remainder behind intent\", \"text\": \"Load non-critical tags on first interaction with an idle-time fallback, after agreeing the measurement impact with the data owner.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F#5-gate-everything-else-behind-intent\" },\n    { \"@type\": \"HowToStep\", \"position\": 6, \"name\": \"Budget in CI and in the field\", \"text\": \"Assert third-party main-thread time and total byte weight on a schedule against production, and alert on INP segmented by tag exposure.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F#validation-and-budgeting\" }\n  ]\n}\n",[607,1642,1643],{"type":1639},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Third-Party Script Performance: Containing Code You Did Not Write\",\n  \"description\": \"Measure what external tags, widgets and embeds cost the main thread, then contain them with facades, deferral, worker isolation and CI budgets.\",\n  \"datePublished\": \"2026-08-01\",\n  \"dateModified\": \"2026-08-01\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F\" }\n}\n",[607,1645,1646],{"type":1639},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    { \"@type\": \"Question\", \"name\": \"Is async or defer better for a third-party tag?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"defer is the safer default: it executes after parsing in document order, so a tag cannot interrupt hydration or an interaction. async executes the moment the file arrives, producing unpredictable long tasks. Reserve async for tags with no ordering requirements that must run as early as possible.\" } },\n    { \"@type\": \"Question\", \"name\": \"Does self-hosting a vendor script make it faster?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"It removes connection setup and lets you cache properly, typically worth 100 to 300 milliseconds on a cold visit, but it does not reduce parse, compile or execution cost. Self-host stable versioned SDKs; leave frequently patched scripts with the vendor.\" } },\n    { \"@type\": \"Question\", \"name\": \"Will a worker sandbox break my analytics?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"It breaks scripts that read the DOM synchronously or need APIs a worker lacks. Measurement tags recording page views and events usually work; UI-rendering scripts usually do not. Validate event-for-event against a control before rollout.\" } },\n    { \"@type\": \"Question\", \"name\": \"How do I prove a specific tag is responsible for an INP regression?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Record the same interaction in the Performance panel with the tag enabled and disabled and compare the bottom-up view for the input handler. Confirm in the field by segmenting INP on visits that did and did not receive the tag.\" } }\n  ]\n}\n",[607,1648,1649],{"type":1639},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"JavaScript Bundle Optimization & Code Splitting\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Third-Party Script Performance\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F\" }\n  ]\n}\n",[1651,1652,1653],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}",{"title":235,"searchDepth":248,"depth":248,"links":1655},[1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666],{"id":191,"depth":248,"text":192},{"id":224,"depth":248,"text":225},{"id":339,"depth":248,"text":340},{"id":448,"depth":248,"text":449},{"id":890,"depth":248,"text":891},{"id":1003,"depth":248,"text":1004},{"id":1280,"depth":248,"text":1281},{"id":1346,"depth":248,"text":1347},{"id":1381,"depth":248,"text":1382},{"id":1535,"depth":248,"text":1536},{"id":1595,"depth":248,"text":1596},"Quantify what external tags, widgets and embeds cost your main thread, then contain them with facades, deferred loading, worker isolation and CI budgets.","md",{"slug":1670,"type":1671,"breadcrumb":1672,"datePublished":1678,"dateModified":1678},"third-party-script-performance","topic",[1673,1675,1676],{"name":1674,"url":772},"Home",{"name":22,"url":21},{"name":5,"url":1677},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002F","2026-08-01","\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance",{"title":1681,"description":1682},"Third-Party Script Performance: Tags, Widgets & Embeds","Measure and contain third-party JavaScript: attribute main-thread cost per origin, apply facades, defer tag managers, sandbox in workers, and enforce a byte budget in CI.","javascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Findex","tnKSmhFBD41gwMLt2PcfIhGvtZSZkinp6-Zszg6uGgI",[1686,1690],{"title":1687,"path":1688,"stem":1689,"children":-1},"Fixing Dual Package Hazard in a Library","\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library","javascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002Findex",{"title":1691,"path":1692,"stem":1693,"children":-1},"Measuring Third-Party Cost with the Coverage Panel","\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Fmeasuring-third-party-cost-with-the-coverage-panel","javascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Fmeasuring-third-party-cost-with-the-coverage-panel\u002Findex",1785622405979]