Bundle Analysis
"The app feels slow" is not actionable. "The pricing page ships a 280 KB moment library just to format one date" is. Bundle analysis is the process of turning a vague sense of slowness into a concrete, fixable list of what's actually inside your JavaScript bundles — and Next.js has an official plugin that makes this a two-minute setup.
Setting up @next/bundle-analyzer
npm install --save-dev @next/bundle-analyzer
Wrap your Next.js config with the analyzer:
// next.config.mjs
import createBundleAnalyzer from '@next/bundle-analyzer'
const withBundleAnalyzer = createBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
/** @type {import('next').NextConfig} */
const nextConfig = {}
export default withBundleAnalyzer(nextConfig)Then run a build with the ANALYZE flag set:
ANALYZE=true npm run build
$env:ANALYZE="true"; npm run build, or use a small cross-platform script with the cross-env package if you want a single cross-platform npm script.The build opens (or writes) interactive treemap HTML reports — one per bundle (client, server, and edge) — showing every module and its size as a proportionally sized rectangle.
What to look for
Unexpectedly large dependencies — a date/time or icon library pulled in wholesale for one small usage.
Duplicate chunks — the same package appearing more than once because different parts of the app import it in incompatible ways.
Client bundles that include code only used on the server, or vice versa, suggesting a missing 'use client' boundary or an accidental import.
Anything you don't recognize — a large chunk you can't explain is worth tracing back to its importing module.
Common fixes once you find a problem
Finding | Fix |
|---|---|
Whole library imported for one function | Switch to a named/subpath import, or a lighter alternative |
Heavy component only needed below the fold | next/dynamic to load it lazily |
Library only used for one interactive feature | Move it behind a 'use client' boundary so it doesn't inflate every page |
Duplicate versions of the same package | Deduplicate via lockfile / package resolutions |
// Before: pulls in the entire library import _ from 'lodash' _.debounce(fn, 200) // After: only the one function is bundled import debounce from 'lodash/debounce' debounce(fn, 200)
Reading the report
The analyzer produces separate reports for the client and server/edge bundles. Focus on the client report first — that's the JavaScript actually downloaded by every visitor's browser. Server bundle size matters for cold start time on serverless platforms, but it doesn't affect the user's perceived page load the same way.