Skip to main content
Back to Articles

Next.js Bundle Analyzer: Optimize Your Build Size

Learn how to use @next/bundle-analyzer to visualize and optimize your Next.js bundle size. Includes real optimization examples and case studies.

March 1, 202614 min readBy Mathematicon

Next.js Bundle Analyzer: Optimize Your Build Size

Bundle size directly impacts your Next.js app's loading speed and performance. The @next/bundle-analyzer plugin helps you visualize and optimize what's in your bundle.

What is Bundle Analysis?

A "bundle" is the compiled JavaScript sent to browsers when users visit your site. Analyzing it helps answer:

  • What's taking up space? Which libraries/code are largest?
  • Why are they included? What imports are pulling them in?
  • Can we reduce it? Which dependencies can be optimized?

Without analysis, you're flying blind. With it, you get visual insights into every byte.

Install @next/bundle-analyzer

npm install --save-dev @next/bundle-analyzer

Configuration: next.config.js

Add this to your next.config.js:

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
  // Your other Next.js config
})

Generate Bundle Report

ANALYZE=true npm run build

This generates:

  • .next/analyze/client.html - Client-side bundle visualization
  • .next/analyze/server.html - Server-side bundle visualization

Open these HTML files in your browser. You'll see an interactive treemap showing what's taking up space.

Understanding the Visualization

The bundle analyzer generates a treemap where:

  • Size of box = How much space that library takes
  • Color = Different packages/modules
  • Hovering = Shows exact size and percentage

Example Output

πŸ“¦ client.js (450 KB)
β”œβ”€β”€ πŸ“¦ react (120 KB) 36%
β”œβ”€β”€ πŸ“¦ lodash (85 KB) 20%
β”œβ”€β”€ πŸ“¦ moment.js (45 KB) 13%
β”œβ”€β”€ πŸ“¦ axios (30 KB) 7%
└── πŸ“¦ your code (170 KB) 24%

Interpretation: React is necessary. But 85 KB for Lodash? That might be worth replacing with a lighter alternative.

Common Bundle Problems & Solutions

Problem 1: Unused Dependencies

Sign: You see a package in the bundle but never use it.

Solution: Remove it

npm uninstall unused-package

Problem 2: Large Date Libraries

Sign: Moment.js, date-fns, or day.js taking up lots of space.

moment.js: 67 KB ❌

Solution: Use lightweight alternative

# Replace moment.js (67 KB)
npm uninstall moment
npm install date-fns  # 13 KB - 5x smaller

Problem 3: Duplicate Dependencies

Sign: Same package appears twice with different versions.

lodash: 30 KB
lodash-es: 32 KB (duplicate!)

Solution: Use npm dedup

npm dedup

Problem 4: Heavy State Management Libraries

Sign: Redux or other state libraries taking up significant space.

redux: 45 KB
react-redux: 35 KB

Solution: Consider lightweight alternatives

# Instead of Redux
npm install zustand  # 2.8 KB
# or
npm install jotai    # 4.3 KB

Problem 5: Unnecessary Polyfills

Sign: Old polyfills included that modern browsers don't need.

Solution: Target modern browsers in next.config.js

module.exports = {
  target: 'es2020', // Modern browsers only
}

Real Example: E-commerce Site

Before Optimization:

Total: 520 KB
β”œβ”€β”€ moment.js: 67 KB
β”œβ”€β”€ lodash: 45 KB
β”œβ”€β”€ jQuery: 30 KB (unused!)
β”œβ”€β”€ old polyfills: 25 KB
└── actual code: 353 KB

After Optimization:

Total: 280 KB (46% reduction!)
β”œβ”€β”€ date-fns: 13 KB (replaced moment)
β”œβ”€β”€ ramda: 24 KB (replaced lodash)
β”œβ”€β”€ (removed jQuery)
β”œβ”€β”€ (removed polyfills)
└── actual code: 243 KB

Result:

  • 240 KB smaller
  • ~500ms faster initial load
  • Better Core Web Vitals scores

Next.js Specific: Code Splitting

Next.js automatically code-splits routes, but you can optimize further:

Automatic Route Splitting

pages/
β”œβ”€β”€ index.js (homepage)
β”œβ”€β”€ about.js (separate bundle)
β”œβ”€β”€ products.js (separate bundle)

Each page gets its own bundle. Users only download the code they need.

Dynamic Imports

// Regular import (included in all pages)
import HeavyComponent from './HeavyComponent'

// Dynamic import (only when needed)
const HeavyComponent = dynamic(() => import('./HeavyComponent'))

For components only needed on specific pages, use dynamic imports:

import dynamic from 'next/dynamic'

const AdminPanel = dynamic(() => import('../components/AdminPanel'), {
  loading: () => <div>Loading...</div>,
})

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <AdminPanel />  {/* Only loaded when needed */}
    </div>
  )
}

External Script Optimization

For third-party scripts (analytics, ads), use next/script:

import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://cdn.example.com/analytics.js" strategy="afterInteractive" />
      <h1>Welcome</h1>
    </>
  )
}

Strategies:

  • beforeInteractive: Load immediately (critical)
  • afterInteractive: Load after page is interactive (normal)
  • lazyOnload: Load when page scrolls (non-essential)

Practical Optimization Checklist

☐ Run: ANALYZE=true npm run build
☐ Identify largest packages
☐ Replace heavy libraries with lighter alternatives
☐ Remove unused dependencies (npm uninstall)
☐ Use dynamic imports for large components
☐ Audit third-party scripts (analytics, ads)
☐ Enable gzip compression
☐ Set appropriate compression level
☐ Monitor bundle size in CI/CD

CI/CD Integration: Monitor Bundle Size

Add to your GitHub Actions or similar:

name: Bundle Size Check

on: [pull_request]

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
      - run: npm install
      - run: ANALYZE=true npm run build
      - name: Upload bundle analysis
        uses: actions/upload-artifact@v2
        with:
          name: bundle-analysis
          path: .next/analyze

Advanced: Custom Bundle Size Limits

Using bundlesize CLI:

npm install --save-dev bundlesize

Create .bundlesizerc.json:

{
  "files": [
    {
      "path": ".next/static/chunks/main.js",
      "maxSize": "250kb"
    },
    {
      "path": ".next/static/chunks/pages/_app.js",
      "maxSize": "200kb"
    }
  ]
}

Fail builds if bundle exceeds limits:

bundlesize

Performance Impact of Bundle Size

Bundle Size β†’ Load Time β†’ User Experience

100 KB:   ~0.5s (excellent)
200 KB:   ~1.0s (good)
300 KB:   ~1.5s (acceptable)
500 KB:   ~2.5s (slow)
1000 KB:  ~5.0s (very slow)

On slow networks, every 100 KB adds ~0.5-1 second.

Best Practices

  1. Analyze regularly - Run analysis on every major change
  2. Set budget limits - Use bundlesize to enforce limits
  3. Code split aggressively - Use dynamic imports for large components
  4. Audit dependencies - Review what each package contributes
  5. Prefer tree-shakeable libraries - Choose ES modules over CommonJS
  6. Use compression - Enable gzip (Next.js does by default)
  7. Monitor in CI/CD - Catch bundle bloat before merging

Real-World Optimization Story

Project: Content management site with 50+ admin pages

Initial Bundle:

  • Total: 1.2 MB (way too big!)
  • React Table: 150 KB (used on 3 pages only)
  • Draft.js: 200 KB (used on 1 page only)
  • moment.js: 67 KB

Optimization Strategy:

  1. Dynamic import React Table only on admin pages
  2. Move Draft.js to dynamic import
  3. Replace moment with date-fns
  4. Remove unused icon library

Results:

  • Main bundle: 1.2 MB β†’ 450 KB (62% reduction!)
  • Admin pages still load fast with dynamic chunks
  • Lighthouse score: 45 β†’ 82

Conclusion

Bundle analysis isn't optionalβ€”it's essential for performance:

  1. Identify bloat with @next/bundle-analyzer
  2. Replace heavy packages with lighter alternatives
  3. Code split with dynamic imports
  4. Monitor with CI/CD integration
  5. Optimize continuously

Even small bundle reductions compound: 50 KB saved = ~200-500ms faster load time.


Learn More

Share this article

Related Articles