Skip to main content
AI
6 min read
February 1, 2026

AI for Performance Optimization: Finding What You Missed in Your Bundle

The Bundle Nobody Inspects

Segev Sinay

Segev Sinay

Frontend Architect

Share:

The Bundle Nobody Inspects

Here is a pattern I see on almost every client project: the team builds features, ships them, and nobody looks at the bundle until the app takes 8 seconds to load on mobile. Then there is a panic, someone runs Lighthouse, sees a 35 on performance, and the team spends two weeks optimizing things they should have caught months ago.

Bundle optimization is not glamorous work. But it is the work that determines whether your users wait 1 second or 5 seconds for your app to load. And AI makes the analysis part fast enough that there is no excuse not to do it regularly.

Here is my process for finding and fixing the performance issues hiding in your JavaScript bundle.

Step 1: Visualize Your Bundle (5 Minutes)

You cannot optimize what you cannot see. Install the bundle analyzer:

# For Vite
npm install -D rollup-plugin-visualizer

# For webpack/CRA
npm install -D webpack-bundle-analyzer

For Vite, add this to your config:

// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    react(),
    visualizer({
      open: true,
      filename: 'bundle-analysis.html',
      gzipSize: true,
    }),
  ],
});

Run the build:

npm run build

This generates an interactive treemap HTML file showing every module in your bundle and its size. Open it and take a screenshot or note the top 10 largest modules.

Step 2: AI Analysis of Bundle Composition (10 Minutes)

Feed your bundle information to Claude Code:

Here is my Vite build output:

dist/assets/index-a1b2c3d4.js    456.78 kB │ gzip: 142.34 kB
dist/assets/vendor-e5f6g7h8.js   823.45 kB │ gzip: 267.89 kB

The largest modules in the bundle (from rollup-plugin-visualizer):
1. node_modules/lodash (78 kB)
2. node_modules/@mui/material (234 kB)
3. node_modules/moment (67 kB)
4. node_modules/chart.js (189 kB)
5. node_modules/firebase (156 kB)
6. node_modules/pdf-lib (123 kB)
7. src/components (89 kB)
8. node_modules/framer-motion (95 kB)

Analyze this bundle and tell me:
1. Which dependencies are oversized for what they provide?
2. What lighter alternatives exist for each heavy dependency?
3. Which modules should be code-split (not needed on initial load)?
4. What is the ideal bundle budget for a SaaS dashboard app?
5. What quick wins would reduce bundle size by 30%+ with minimal effort?

Claude Code will identify the obvious problems: full lodash instead of individual imports, moment.js instead of date-fns or dayjs, entire MUI library when you use 5 components. These are the quick wins.

Step 3: Find and Fix Heavy Imports (15 Minutes)

The biggest bundle bloat usually comes from imports that pull in more than you need:

Search the entire src/ directory for import patterns that cause bundle bloat:

1. Barrel imports from large libraries:
   import { Button } from '@mui/material'  (pulls in entire MUI)
   vs import Button from '@mui/material/Button' (only Button)

2. Full lodash imports:
   import { debounce } from 'lodash' (pulls in entire lodash)
   vs import debounce from 'lodash/debounce' (only debounce)

3. Icon library imports:
   import { FaUser } from 'react-icons/fa' (may pull in all FA icons)
   vs import { FaUser } from 'react-icons/fa6' (tree-shakeable)

4. Moment.js usage (should be replaced with date-fns or dayjs)

5. Any dynamic import that is actually a static dependency

For each issue found, show me the file, current import, and the optimized import.
Generate a codemod script that fixes all of them at once.

On one client project, changing lodash imports from barrel to direct reduced the bundle by 65 kB. Changing MUI imports saved another 120 kB. These two changes alone cut load time by almost a full second on 4G connections.

Step 4: Implement Code Splitting (15 Minutes)

Not everything needs to be in the initial bundle. Identify candidates for lazy loading:

Read my React Router configuration in src/App.tsx (or src/routes/).

Identify routes that should be code-split with React.lazy():

Criteria for lazy loading:
- Routes that are not the landing/home page
- Routes that require authentication (user is already waiting for auth check)
- Routes with heavy dependencies (charts, editors, PDF viewers)
- Admin-only routes
- Routes accessed less than 20% of sessions

For each route to be lazy loaded:
1. Show the current import
2. Show the lazy import replacement
3. Show the Suspense wrapper with a loading fallback
4. Estimate the bundle size reduction

Also check for components within pages that could be lazy loaded:
- Charts that render below the fold
- Modals that open on user action
- Rich text editors
- File uploaders

Here is the typical transformation:

// Before
import Dashboard from './pages/Dashboard';
import Settings from './pages/Settings';
import Analytics from './pages/Analytics';
import AdminPanel from './pages/AdminPanel';

// After
import Dashboard from './pages/Dashboard'; // Keep eager — it's the landing page
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() => import('./pages/Analytics'));
const AdminPanel = lazy(() => import('./pages/AdminPanel'));

// In JSX
<Suspense fallback={<PageSkeleton />}>
  <Routes>
    <Route path="/dashboard" element={<Dashboard />} />
    <Route path="/settings" element={<Settings />} />
    <Route path="/analytics" element={<Analytics />} />
    <Route path="/admin" element={<AdminPanel />} />
  </Routes>
</Suspense>

Step 5: Optimize Images and Assets (10 Minutes)

Bundle analysis often reveals that developers are importing images directly into JavaScript:

Search the codebase for:
1. Image imports in JS/TS files (import logo from './logo.png')
2. Large SVGs imported as React components
3. Base64-encoded images in CSS or JS
4. Images in the public folder that are not optimized

For each issue:
- What is the current file size?
- What format should it be? (WebP for photos, SVG for icons, AVIF for large images)
- Should it be lazy loaded (below the fold)?
- Should it use next/image or an img tag with loading="lazy"?

Step 6: Set Up Bundle Budgets (5 Minutes)

Prevent regression by setting size limits:

// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom', 'react-router-dom'],
          charts: ['chart.js', 'react-chartjs-2'],
          ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
        },
      },
    },
    chunkSizeWarningLimit: 250, // Warn if any chunk exceeds 250 kB
  },
});

Add a CI check:

#!/bin/bash
# scripts/check-bundle-size.sh
MAX_MAIN_SIZE=200  # kB
MAX_VENDOR_SIZE=300  # kB

MAIN_SIZE=$(stat -f%z dist/assets/index-*.js | awk '{print int($1/1024)}')
VENDOR_SIZE=$(stat -f%z dist/assets/vendor-*.js | awk '{print int($1/1024)}')

if [ "$MAIN_SIZE" -gt "$MAX_MAIN_SIZE" ]; then
  echo "FAIL: Main bundle ($MAIN_SIZE kB) exceeds budget ($MAX_MAIN_SIZE kB)"
  exit 1
fi

if [ "$VENDOR_SIZE" -gt "$MAX_VENDOR_SIZE" ]; then
  echo "FAIL: Vendor bundle ($VENDOR_SIZE kB) exceeds budget ($MAX_VENDOR_SIZE kB)"
  exit 1
fi

echo "PASS: Bundle sizes within budget"

Real Impact

On a recent SaaS dashboard project, this process reduced:

  • Initial JavaScript: from 1.2 MB to 380 kB (gzipped: 420 kB to 128 kB)
  • Time to Interactive: from 6.2 seconds to 2.1 seconds on 4G
  • Lighthouse Performance: from 42 to 89

The three biggest wins were: replacing moment.js with dayjs (saved 62 kB), fixing MUI barrel imports (saved 180 kB), and code splitting three heavy routes (saved 340 kB from initial load).

None of these changes took more than 30 minutes. The AI-powered analysis took 15 minutes. Total optimization time: one afternoon for a 3x performance improvement.

AI
React
Productivity
TypeScript
Performance
Technical Leadership
Build Tools
Bundle Optimization

Related Articles

Contact

Let’s Connect

Have a question or an idea? I’d love to hear from you.

Send a Message