DEV Community

Cover image for How I Cut JS Bundle Size by 83% and Made a Page Load 3x Faster
Akash Paul
Akash Paul

Posted on

How I Cut JS Bundle Size by 83% and Made a Page Load 3x Faster

Our app was embarrassingly slow.

13.5 seconds to First Contentful Paint on mobile. A Lighthouse score of 47. Users were waiting — and probably leaving.

I'd just joined as the frontend engineer at a SaaS startup. Nobody told me to fix the performance. But the numbers were bad enough that I couldn't ignore them.

Here's exactly what I did, and what I learned.


The Starting Point (It Was Bad)

Before I touched anything:

Metric Score
JS Bundle 2,054 KB (~2MB)
CSS 175 KB
First Contentful Paint 13.5 seconds
Lighthouse Mobile 47 / 100
Lighthouse Desktop 60 / 100

A 2MB JavaScript bundle. On mobile. In 2025.

This wasn't a slow app — it was a broken experience. Nobody waits 13 seconds for a dashboard to load.


Finding the Culprit

First step: understand why it's this bad. I ran a bundle analysis and found three main problems.

1. Everything was loading upfront

The app had no code splitting. Every route, every modal, every component — all of it loaded on the very first page visit. Users were downloading code for screens they'd never even open.

2. A massive unused script: gptengineer.js

This was the biggest single win. Someone had added a heavy script from an AI prototyping tool during early development and it never got removed. It was sitting in the bundle, loading on every page, doing absolutely nothing in production.

This single file was responsible for a significant chunk of that 2MB.

3. CSS bloat

175KB of CSS — most of it unused. Styles imported globally, never cleaned up, just accumulating.


The Fixes (Nothing Exotic)

I want to be honest: I didn't use any fancy techniques here. These are standard practices. The problem was they just hadn't been applied yet.

Fix 1: Lazy loading routes and heavy components

// Before — loads everything immediately
import Dashboard from './pages/Dashboard'
import CampaignManager from './pages/CampaignManager'
import Analytics from './pages/Analytics'

// After — loads only when needed
const Dashboard = lazy(() => import('./pages/Dashboard'))
const CampaignManager = lazy(() => import('./pages/CampaignManager'))
const Analytics = lazy(() => import('./pages/Analytics'))
Enter fullscreen mode Exit fullscreen mode

Wrap with <Suspense> and you're done. React does the heavy lifting — it splits each lazy import into its own chunk and only fetches it when the user navigates to that route.

The impact was immediate. The initial bundle dropped significantly because users on the login page no longer needed to download the entire dashboard.

Fix 2: Remove dead code ruthlessly

gptengineer.js — gone. It was one line to remove it. That one line recovered hundreds of KB.

This is a lesson I'll carry forward: audit your dependencies regularly. Dev tools leave artifacts. Prototyping libraries get committed. Nobody notices until someone runs a bundle analysis.

# Run this. Look at what's in your bundle.
npx vite-bundle-visualizer
# or for webpack:
npx webpack-bundle-analyzer
Enter fullscreen mode Exit fullscreen mode

Fix 3: Clean up global CSS imports

Instead of importing everything globally, I scoped styles to where they were actually used and removed entire CSS files that had no live references in the codebase.

This took the CSS from 175KB → 40KB. A 77% reduction just from cleanup.


The Results

After three focused days of work:

Metric Before After Change
JS Bundle 2,054 KB 359 KB 83% smaller
CSS 175 KB 40 KB 77% smaller
First Contentful Paint 13.5s 4.4s 3x faster
Lighthouse Mobile 47 68 +21 points
Lighthouse Desktop 60 96 +36 points

Desktop went from 60 to 96. Mobile from 47 to 68.

The app felt like a different product.


What I'd Tell My Past Self

Measure first, always. I knew the app was slow but I didn't know why until I ran the bundle analysis. Guessing wastes time.

The basics compound. Code splitting + removing dead code + cleaning CSS. Nothing groundbreaking. But each one reinforced the others.

Performance is everyone's job. It took me joining the team for this to get fixed. That's too late. Bundle audits should be part of regular dev cycles — not something discovered by accident.

The 2MB was embarrassing in hindsight, but that's fine. Fast-moving startups ship features first and optimize later. The key is knowing when "later" has arrived.


Quick Checklist if You're in the Same Situation

  • [ ] Run a bundle visualizer — find what's actually in there
  • [ ] Search for dev/prototyping tools left in production (gptengineer, lovable, etc.)
  • [ ] Add React.lazy() + Suspense for route-level code splitting
  • [ ] Check for globally imported CSS with no live references
  • [ ] Set a bundle size budget in your build config so this doesn't creep back

If you've got a React app that feels sluggish and haven't run a bundle analysis yet — do it today. You might be surprised what you find.

Happy to answer questions in the comments. What's the worst bundle size you've seen in the wild?

Top comments (0)