JavaScript is the most expensive resource on a web page. Not just for the bytes it transfers, but for the CPU time it consumes when being parsed, compiled and executed. The real problem is that between 35% and 45% of the JavaScript loaded by the average website is never executed during the user’s first interaction.
Removing that unnecessary code directly improves the web speed metrics Google uses as ranking signals: LCP, INP and total load time.
Why unused JavaScript damages your Core Web Vitals
When the browser downloads a JavaScript file, it must do three things before the code becomes functional: download it, parse it and execute it. Each of these phases consumes time and resources.
According to web.dev, every 100KB of JavaScript adds between 150 and 300ms of processing time on a mid-range mobile device. A smartphone with a Snapdragon 680 processor processes JavaScript between 3 and 5 times slower than a MacBook Pro.
The impact on Core Web Vitals is threefold:
- LCP: render-blocking scripts delay the painting of the main content. A synchronous script in the head can add 500ms or more to the LCP.
- INP (Interaction to Next Paint): the browser’s main thread cannot process user interactions while executing JavaScript. The more JS that executes, the greater the delay between the user’s click and the visual response.
- TBT (Total Blocking Time): JavaScript tasks lasting more than 50ms block the main thread. Accumulated TBT correlates directly with a poor interactive experience.
JavaScript code that is never executed during the initial load still consumes parse and compile time. The browser cannot know in advance which functions will run, so it processes the entire file.
How to identify unused JavaScript with Chrome DevTools
Chrome DevTools includes the Coverage tool, which measures what percentage of each CSS and JavaScript file is actually executed during loading and interaction.
Steps to use it:
- Open Chrome DevTools (F12).
- Open the Coverage panel: Ctrl+Shift+P > type “Coverage” > select “Show Coverage”.
- Click the reload button to start recording from the page load.
- Interact with the page (scroll, menu clicks, forms) to simulate real usage.
- Observe the “Usage Visualization” column: red bars indicate unexecuted code.
What to look for:
- Files with more than 50% unused code: these are candidates for optimization or removal.
- Large third-party bundles: complete libraries loaded to use a single function.
- Unnecessary polyfills: compatibility code for browsers your users don’t use.
The Network tab complements this analysis by showing the size of each downloaded JavaScript file and whether it blocks rendering (the “Initiator” column and the “render-blocking” marker).
Third-party JavaScript: the worst offender (and how to manage it)
Third-party scripts account for 57% of total JavaScript on commercial websites according to HTTP Archive. Analytics, chatbots, remarketing pixels, social media widgets, A/B testing tools: each one adds its JavaScript payload to the main thread.
The problem is not just the weight. Each third-party script can:
- Make additional HTTP requests that compete for bandwidth.
- Execute code on the main thread that blocks interactivity.
- Load its own dependencies (a chat widget may bring in all of React even if your site does not use it).
- Fail silently and leave orphaned listeners that consume memory.
Strategies for managing third-party scripts
Audit regularly: review each third-party script and ask whether you really need it. A pixel from a campaign that ended 6 months ago is still executing on every page load.
Consolidate with Google Tag Manager: GTM allows you to manage multiple scripts from a centralized interface. Although GTM has its own performance cost (~30-80KB), consolidating 5-10 individual scripts into GTM is usually more efficient.
Load after interaction: for non-critical scripts (live chat, surveys), delay loading until the user interacts with the page. A scroll or click listener that injects the script on demand removes its impact from the initial load.
Use facades: a static image or button that simulates the appearance of the real widget. When the user clicks, the full script loads. YouTube embed is the classic example: a thumbnail with a play button that loads the iframe only on click.
Tree shaking and code splitting: removing dead code in the build
Tree shaking is an optimization technique that analyses your code’s imports and removes functions and modules that are not used. Modern bundlers (webpack, Rollup, esbuild, Vite) implement it natively when you use ES Modules (import/export).
For tree shaking to work correctly:
- Use
import { specificFunction } from 'library'instead ofimport * as library from 'library'. - Ensure dependencies have
"sideEffects": falsein their package.json, or declare side effects explicitly. - Avoid re-exporting entire modules in barrel files (index.ts that re-exports everything).
Code splitting divides your JavaScript into chunks that are loaded on demand. Instead of a monolithic 500KB bundle, you generate 50-100KB chunks that only load when a route or feature needs them.
Modern frameworks implement code splitting automatically:
- Astro uses island architecture: interactive components only hydrate when they are visible (
client:visible), eliminating all JavaScript from static components. - React with lazy(): allows loading components on demand with
React.lazy()andSuspense. - Dynamic imports:
import('./module.js')loads a module only when that line of code executes.
The combination of tree shaking and code splitting can reduce initial JavaScript by 40% to 70%, depending on the original bundle size.
Defer and async loading: defer vs async
The defer and async attributes on the <script> tag control when JavaScript is downloaded and executed. Using them correctly prevents scripts from blocking HTML rendering.
Without attributes (<script src="app.js">): the browser stops parsing HTML, downloads the script, executes it and then continues with the HTML. This is the worst scenario for performance.
async (<script async src="app.js">): the browser downloads the script in parallel with HTML parsing, but when the download finishes it stops parsing to execute it. Execution order is not guaranteed between scripts with async.
defer (<script defer src="app.js">): the browser downloads the script in parallel with HTML parsing and executes it after the HTML has been fully parsed. Execution order is respected between scripts with defer.
When to use each
- defer: for most scripts. It ensures HTML is parsed without interruptions and scripts execute in order. This is the recommended default.
- async: for independent scripts that do not depend on the DOM or other scripts, such as analytics or tracking pixels.
- Without attributes: only for critical scripts that must execute before content is visible (very exceptional cases).
For scripts with known JavaScript performance issues, combining defer with code splitting ensures the main thread remains free to respond to user interactions.
Frequently asked questions about JavaScript and performance
How much JavaScript is too much for a website?
There is no absolute limit, but Google recommends keeping total JavaScript below 300KB compressed for a good mobile experience. Sites exceeding 1MB of uncompressed JS typically suffer from INP and load time issues.
Do WordPress plugins add a lot of unnecessary JavaScript?
Yes. Each WordPress plugin can add between 20KB and 200KB of JavaScript. The worst offenders are page builders, sliders, live chat and social media plugins. The solution is to audit which plugins load JS, disable unnecessary ones and use lightweight alternatives.
How do I load third-party scripts without hurting my performance?
Use the defer attribute for scripts that are not critical during the initial load. For heavy scripts (chat, advanced analytics), delay their loading until the user’s first interaction. For tracking scripts, evaluate whether Google Tag Manager can consolidate several into one.
Reducing unnecessary JavaScript is one of the highest-impact optimizations for Core Web Vitals. If you want us to analyse the JavaScript weight and execution on your site and help you implement an optimization strategy, contact us.
Share this article
If you found this content useful, share it with your colleagues.
Frequently Asked Questions
¿Google puede rastrear sitios web con mucho JavaScript?
Sí, Google puede rastrear JavaScript, pero con limitaciones. Para optimizar usa server-side rendering, implementa hydration progresiva, asegura que el contenido crítico sea accesible sin JS y usa técnicas como lazy loading responsable.
¿Qué es mejor para SEO: SPA o páginas tradicionales?
Las páginas tradicionales suelen ser mejores para SEO por su facilidad de crawling. Las SPAs requieren configuración adicional (SSR, prerendering) pero pueden ofrecer mejor UX. La elección depende de tus objetivos específicos y recursos técnicos.
¿Con qué frecuencia publican contenido nuevo?
Publicamos artículos nuevos semanalmente, enfocados en las últimas tendencias de SEO técnico, casos de estudio reales y mejores prácticas. Suscríbete a nuestro newsletter para no perderte ninguna actualización.
¿Los consejos son aplicables a cualquier tipo de sitio web?
Nuestros consejos se adaptan a diferentes tipos de sitios: ecommerce, blogs, sitios corporativos y aplicaciones web. Siempre indicamos cuándo una técnica es específica para cierto tipo de sitio o requerimientos técnicos.