How JavaScript Slows Down Websites
JavaScript is the biggest reason production websites feel slow. Here's how it kills your score and the systematic way to reduce it.
Table of contents
- The hidden cost of JavaScript
- Bytes vs CPU cost
- Main thread blocking
- Render blocking
- The five worst JavaScript offenders
- 1. Tag managers
- 2. Analytics
- 3. A/B testing tools
- 4. Chat widgets
- 5. Embedded videos
- How to cut your JavaScript
- Audit what you ship
- Replace heavy libraries
- Code-split
- Defer everything you can
- Use SSR or SSG
- Reduce re-renders
- Measuring the impact
- The 80/20
- When JavaScript is fine
- The bottom line
JavaScript is the single biggest reason real-world websites are slow. It's downloaded, parsed, compiled, and executed — all on the user's CPU, which on mobile is roughly 4× slower than on a developer's laptop.
Here's exactly how JavaScript hurts your site and the systematic way to cut it back.
The hidden cost of JavaScript
Bytes vs CPU cost
A 200KB image takes 200KB of bandwidth. That's it. The browser displays it and moves on.
A 200KB JavaScript bundle takes 200KB of bandwidth — plus parse time, compile time, and execution time. On a mid-range Android phone, processing 200KB of JS takes roughly 400ms. On the same phone, processing 1MB takes 2+ seconds.
That's why halving your JS bundle often gives a bigger speed boost than halving your image weight.
Main thread blocking
JavaScript runs on the main thread — the same thread that paints frames and responds to clicks. While JS is executing, the browser can't:
- Respond to user input (kills your INP)
- Render new frames
- Process other tasks
A single 200ms JavaScript task makes the whole page feel laggy.
Render blocking
Synchronous <script> tags block HTML parsing. Until the script downloads and runs, the rest of the page sits unprocessed.
<!-- ❌ Blocks parsing until script downloads + runs -->
<script src="/big.js"></script>
<!-- ✅ Downloads in parallel, runs after parse -->
<script src="/big.js" defer></script>
<!-- ✅ Downloads in parallel, runs ASAP (no order guarantee) -->
<script src="/big.js" async></script>
The five worst JavaScript offenders
1. Tag managers
Google Tag Manager (GTM), Tealium, etc. ship a small loader that pulls in more JavaScript at runtime — often hundreds of KB of analytics, A/B test, and marketing scripts. Each one runs on your main thread.
Fix: Question whether you need GTM at all. If you do, prune the tags you don't actively use, and load GTM with async.
2. Analytics
Google Analytics, Mixpanel, Segment, Heap, FullStory — each adds 30–200KB of JS and event handlers that run on every interaction.
Fix: Pick one. Use server-side analytics if possible (Plausible has both, PostHog has reverse-proxy). Avoid stacking multiple analytics platforms.
3. A/B testing tools
Optimizely, VWO, Google Optimize (RIP) — these are notorious for "flicker" because they delay rendering until the test variant loads. Often 100+ KB of script that blocks paint.
Fix: Run tests server-side or at the edge (Vercel Edge Config, Cloudflare Workers). Avoid client-side A/B test platforms if you can.
4. Chat widgets
Intercom, Drift, HubSpot chat — typically 200–500KB of script and CSS. They almost never need to load before user interaction.
Fix: Lazy-load the widget. Initialize it only when the user clicks a "Chat with us" button.
5. Embedded videos
A YouTube embed pulls in 500KB+ of JS even if the user never plays the video.
Fix: Use a lite-youtube-embed or facade pattern — show a thumbnail and only load the real player on click.
How to cut your JavaScript
Audit what you ship
In Chrome DevTools → Coverage tab → record a page load. You'll see how much of each JS file is actually used on the page. Most sites discover they're shipping 50–70% unused code.
In Lighthouse, the "Reduce unused JavaScript" opportunity tells you the same thing with byte counts.
Replace heavy libraries
| Heavy | Alternative | Savings |
|---|---|---|
| Moment.js (300KB) | date-fns or native Intl | 280+ KB |
| Lodash (70KB) | native ES methods or lodash-es per-method | 60+ KB |
| jQuery (90KB) | vanilla JS, modern DOM APIs | 90 KB |
| Bootstrap JS (60KB) | Tailwind + small JS, or no framework | 60 KB |
| Axios (15KB) | native fetch | 15 KB |
For libraries you do need, import only what you use (tree-shaking):
// ❌ Imports entire library
import _ from "lodash";
// ✅ Imports only what's needed (tree-shaken)
import { debounce } from "lodash-es";
Code-split
Don't ship the JavaScript for every page on every page. Use dynamic imports:
// Loaded only when needed
const Chart = await import("./Chart");
In Next.js, next/dynamic makes this easy. In React, React.lazy does the same.
Defer everything you can
If a script isn't required for first paint, give it defer or async. Run analytics, marketing pixels, and chat scripts after the page is interactive.
Use SSR or SSG
A statically-rendered page can ship far less JavaScript than a fully client-rendered one. Next.js (with SSG or SSR), Astro, and Hugo are examples of frameworks that ship minimal JS by default.
Reduce re-renders
If you're using React/Vue/etc., re-renders are JS work on the main thread. Use:
React.memoto prevent unnecessary re-rendersuseMemoanduseCallbackfor stable references- Component splitting so state changes don't ripple through the whole tree
Measuring the impact
After cutting JS, watch these Lighthouse metrics:
- TBT (Total Blocking Time) should drop — usually proportional to KB removed
- INP should improve — see INP explained
- LCP may improve if you removed render-blocking scripts
- Overall Performance score typically jumps 5–20 points per major change
The 80/20
If you have one hour to make your site faster, do these:
- Defer or async every
<script>tag that isn't critical to first render - Remove one third-party script you don't actively use (analytics duplicates, chat widgets, A/B tools)
- Replace Moment.js with date-fns (or similar one-library swap)
- Code-split your largest JS bundle by route
That's typically a 15–30 point performance score gain.
When JavaScript is fine
Not every site needs to be JS-light. Web apps (Figma, Linear, Google Docs) live or die by their JavaScript. Their users tolerate longer initial loads in exchange for the functionality.
But marketing pages, blogs, e-commerce product pages, and content sites should ship minimal JS. Use the right tool for the job.
The bottom line
JavaScript is unique among web resources because it costs CPU time on top of bandwidth. Every 100KB of unused JS is parse, compile, and execution time that adds nothing to your user experience.
Audit what you ship. Cut what you don't need. Defer what's left. Run a free audit and watch your score climb.
For more on improving overall site speed, see how to improve your PageSpeed Insights score and why is my website so slow.
Frequently Asked Questions
Is JavaScript bad for SEO?+
Not inherently. Google can render JavaScript. But heavy JavaScript hurts Core Web Vitals (TBT, INP, LCP), which are ranking signals. Less JavaScript usually means better SEO.
Should I remove all third-party scripts?+
No, but audit them. Most sites can remove 30–50% of their third-party scripts without losing meaningful functionality. The remaining ones should be loaded async and ideally fire after user interaction.
Does using React or Next.js make my site slow?+
Not necessarily. Server-side rendering (SSR) and static generation (SSG) in Next.js can produce sites that are as fast as static HTML. The performance problem starts when you ship a lot of client-side JavaScript and re-renders.
See how your site really performs
Run a full website health check on mobile and desktop in 30 seconds — no signup needed.