We help companies like yours with expert Next.js performance advice and auditing.

nextjs, bundle size
09/07/2025 04:17

Stop Shipping Bloat: Practical Bundle Diet for Next.js Developers

Slim down your Next.js bundles with leaner libraries, tree-shakable imports, and built-in analysis tools for faster, more responsive apps.

media

Next.js applications can suffer performance hits when they ship too much JavaScript. Large bundles increase download, parse and execution time, delaying First Contentful Paint (FCP) and Time to Interactive (TTI). That also pushes back ad slots and other third-party scripts.

Why we quote minified size only

All size figures in this article refer to the minified but uncompressed footprint of a package as reported by Bundlephobia (sizes verified in July 2025). While on‑the‑wire gzip/brotli can shrink bytes transferred, the browser still has to parse every minified byte, so parse & execution cost correlate with the minified size. Using a single metric keeps comparisons clear and avoids mixing incomparable numbers.

Replacing Large Libraries with Smaller Alternatives

Third‑party libraries often dominate a bundle. Replacing a heavyweight dependency with a lighter API‑compatible one can dramatically cut size.

Library Size Reduction
moment 295 kB baseline
date‑fns (v4) 77 kB - 76 %
dayjs 7 kB - 96 %

Beyond its large size and mutable API, moment is now considered a legacy project by its creators, who recommend against its use in new development. A modern alternative like dayjs is not only dramatically smaller but also maintains a very similar API, making migration straightforward and highly recommended.

Use Bundlephobia before npm install

Bundlephobia shows install size, minified size, dependency tree, and export surface. Paste a prospective package into the search to see its raw cost and compare alternatives.

Ask LLMs for Alternatives

Prompt your favourite LLM with a list of current client‑side dependencies and ask for smaller replacements, opportunities to move the work server‑side, and migration effort estimates.

I have the following client-side libraries in a next.js app:
{copy-paste your libraries here}
examine each library and let me know if:
* we can get rid of any of them to save up on bundle size
* suggest alternative libraries to move some things to the server side
* suggest alternative libraries that has same functionality but has smaller bundle size
* if an alternative library is provided consider how hard it is to migrate to that library and keep in mind how much we can reduce the minified bundle size by this

Such queries can yield suggestions like Day.js, date-fns, or Luxon as alternatives to Moment.js Using AI in this way can jump-start your search for performance-friendly libraries.

Avoiding Wildcard (import *) Imports

Wildcard (“star”) imports sabotage tree‑shaking for CommonJS packages because the bundler has to keep the entire export object alive at runtime. The hit is less obvious with pure‑ESM libraries, but any namespace import still forces extra property enumeration overhead. Although pure-ESM libraries are still not that common.

Named imports tree‑shake best - they give the bundler static knowledge of what is used.

// Good - pulls in just debounce from lodash/debounce (~1.8 kB)
import debounce from 'lodash/debounce';
// Good with ESM libs - only the function is kept
import { format } from 'date-fns';

Star imports block dead‑code elimination - every export is considered a live property.

// Bad - includes the whole library (~70 kB)
import * as _ from 'lodash';

Tooling fixes

  • ESLint - add import/no-namespace to fail on star imports.
  • Side‑effects declarations - ensure internal libraries list "sideEffects": false in package.json so bundlers can drop unused files.

Lodash Optimisation and Tree‑Shaking

Lodash remains popular for its stable API and rich utility set, but the monolithic lodash package (~70 KB minified) is rarely justified in 2025 apps. Choose one optimisation strategy and apply it consistently across the codebase:

Strategy How Pros Cons
Per‑method import import debounce from 'lodash/debounce' Zero config, explicit Verbose paths, must know file locations
lodash-es + tree‑shaking import from 'lodash-es’ Clean syntax, auto‑drops unused helpers Requires ESM‑aware bundler; potential CJS duplicates - see the Watch out for duplications for more details
Alias lodash => lodash-es webpack/Turbopack alias: "lodash": "lodash-es" One‑line migration Node (CJS) code may break (e.g. Server-side or tooling scripts that still use require('lodash'))
Babel/SWC transform babel-plugin-lodash / @swc/plugin-transform-imports Automated, works with existing CJS imports Build‑time dependency, plugins are not maintained anymore

Sample alias in Next.js

Next.js >= 14 uses Turbopack in dev and webpack 5 in production, so wire the alias into both toolchains:

// next.config.js (CommonJS)
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Production (webpack)
  webpack(config) {
    config.resolve.alias = {
      ...(config.resolve.alias || {}),
      lodash: 'lodash-es',
    };
    return config;
  },
  // Development (Turbopack dev)
  experimental: {
    // Deprecated in Next 15 – switch to top‑level `turbopack` once you upgrade
    turbo: {
      resolveAlias: {
        lodash: 'lodash-es',
      },
    },
  },
  // Forward‑looking config (Next 15 full Turbopack build)
  turbopack: {
    resolveAlias: {
      lodash: 'lodash-es',
    },
  },
};
module.exports = nextConfig;

If you’re on the Next 15 canary (full Turbopack build), only the experimental.turbo.resolveAlias block is needed.

Watch out for duplication

lodash-es saves on bundle size only when no other code path pulls in CommonJS Lodash. Duplicates creep in when:

  • Legacy imports in your codebase still do require('lodash') or import _ from 'lodash'.
  • Third‑party dependencies somewhere in node_modules require CJS Lodash.
  • Mixed strategies - teammates import lodash/debounce while others use lodash-es.
  • Aliasing gaps - you alias in Turbopack/webpack but forget Jest/Vite, so test bundles pull CJS Lodash.

How to detect & fix

  1. Analyse the bundle: the best approach is to use the official, integrated solution: @next/bundle-analyzer. After installing it, you can enable it in your next.config.js file. This will automatically generate a visual map of your client and server bundles during the build process, helping you pinpoint exactly where the bloat is coming from.
  2. List dependents: npm ls lodash shows which packages pull CJS Lodash.
  3. Patch stubborn deps with patch-package or submit a PR upstream.

Even one stray require('lodash') can add ~70 KB minified, so treat duplicates as a CI‑failing lint error.

By thoughtfully applying these techniques - from picking lighter libraries to fine-tuning imports - you can achieve substantial performance gains. Modern web apps demand speed, especially on mobile devices and you have many tools at your disposal to trim the fat from your Next.js bundles. Measure your improvements (use Lighthouse, Web Vitals, or Next.js’s build size analysis) and iterate. The end result will be a faster, snappier application that delights users and makes the most of what Next.js has to offer.

If you're concerned about your site's bundle sizes and want to ensure the best possible performance, reach out to us at Catch Metrics! We can help.

Get Your Free Consultation Today

Don’t let a slow Next.js app continue to impact your commercial goals. Get a free consultation
with one of our performance engineers and discover how you can achieve faster load times,
happier customers, and a significant increase in your key commercial KPIs.