# Third-party JavaScript: what it really costs

A synchronous third-party script in the head moved first paint by 388 ms in the lab: 296 ms without it, 684 ms with lodash, 284 ms deferred. Here is the cost.

> Page: https://ui.eneawork.it/blog/performance-terze-parti-js
> Markdown: https://ui.eneawork.it/blog/performance-terze-parti-js.md

*2026-11-01*

Uno script di terze parti sincrono in testa ha spostato il primo paint di 388 ms: nella prova senza script il FCP è 296 ms, con lodash.min.js in testa è 684 ms. Lo stesso file con defer lascia il FCP a 284 ms, quindi il costo non è il file, è il momento in cui lo carichi.

Tre pagine di prova identiche, servite da un server statico locale e misurate cinque volte ciascuna in Chrome 155 headless con puppeteer, rete Slow 4G simulata (1,6 Mbps, 150 ms di RTT) e CPU rallentata 4 volte. Senza script FCP 296 ms; con lodash.min.js sincrono in testa FCP 684 ms; con lo stesso file in defer FCP 284 ms. Il file trasferisce 27.051 byte (26.751 byte gzip letti con curl). Comando: node scripts/perf-lab.js http://localhost:3200/js-sync.html 5. Connessione a freddo verso cdn.jsdelivr.net letta con curl: 17 ms di DNS, 34 ms di TCP, 63 ms di TLS, 86 ms al primo byte. Sul nostro sito le tre pagine misurate (home, articolo, /components) fanno 0 richieste a host terzi. Il catalogo misura 111.985 componenti, 1.556 blocchi e 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026.

## Tre varianti, un solo file

Il divario tra sincrono e defer è 400 ms di FCP: con lo script sincrono il parser si ferma prima di dipingere. I byte delle due varianti differiscono per come il server ha compresso la risposta, non per il contenuto.

## Il costo che non si vede

Un host terzo paga un handshake che la connessione già aperta verso la tua origine non può riusare: nella nostra misura 63 ms di TLS e 86 ms al primo byte, prima di scaricare un solo byte utile. Poi arrivano i byte del file: 26.751 per lodash, 153.653 per un tag di Google Analytics demo, letti con curl lo stesso giorno.

for u in https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js \ "https://www.googletagmanager.com/gtag/js?id=G-DEMO123"; do curl -s -o /dev/null -H "Accept-Encoding: gzip, br" \ -w "$u dns=%{time_namelookup} tls=%{time_appconnect} ttfb=%{time_starttransfer} bytes=%{size_download}\n" "$u" done

Il comando stampa una riga per host: DNS, fine del TLS, primo byte e byte trasferiti. Il tempo che conta è tls, perché è quello che la tua pagina paga prima di poter chiedere il file.

## Come si riduce

## Il limite da dichiarare

La prova misura un file, una pagina e una rete simulata: un sito con sei tag non paga sei volte lo stesso ritardo, perché le connessioni si riusano e gli script si sovrappongono. defer toglie il blocco del parser ma non il lavoro sul thread principale, e un long task da 61 ms è comparso comunque in una delle cinque run sincrone. Consenso, privacy e cookie banner hanno un costo che qui non è misurato.

## Dove continuare

Il catalogo EneUI indicizza 111.985 componenti, l'hero horizon-hero-section e l'icona icon-access-point: un hero con video e un font in più pesa più di uno script di analytics. Leggi come si fissa un performance budget, il bundle splitting e quanto pesa una UI library.

## FAQ

### defer e async sono equivalenti?

No. defer conserva l'ordine tra gli script ed esegue dopo il parsing; async esegue appena il file arriva e può anticipare uno script che dipendeva da un altro.

### Quanto costa un tag di analytics?

Da 26.751 byte per una piccola libreria a 153.653 byte per gtag, più un handshake verso un host terzo. Il tempo, non il peso, decide il primo paint.

### Il preconnect serve sempre?

Serve quando usi davvero l'host e la connessione si apre abbastanza presto. Su host che chiami di rado aggiunge solo una connessione da chiudere.

### Meglio self-host o CDN pubblica?

Self-host vince sul primo byte e sul controllo, perde sull'aggiornamento automatico. Per uno script che cambi di rado, self-host con cache lunga è la scelta più semplice.

### Come misuro il costo sul campo?

Con il Resource Timing per i byte e con i long task per il thread principale. Il laboratorio dice quando carichi; i dati di campo dicono quanto pesa per i tuoi utenti.
