How to reduce JavaScript bundle size
Every kilobyte of JavaScript is downloaded, parsed and run before a page responds. Measure the bundle, split it by route, lazy load the rest and trim dependencies.
JavaScript is the most expensive thing a page ships. An image only has to be decoded. A script has to be downloaded, parsed, compiled and executed, all on the main thread, before the page reacts to a tap. On a mid-range phone, a large bundle is the difference between a page that responds and one that feels stuck.
Here is the order we work in.
1. Measure before you cut
You cannot shrink what you cannot see. A bundle analyzer draws the bundle as a map of boxes, one per module, sized by weight.
- Webpack:
webpack-bundle-analyzer - Vite and Rollup:
rollup-plugin-visualizer - Any build with source maps:
source-map-explorer
The surprises are usually the same: a date library with every locale, a full icon set, or two versions of the same package.
2. Split the code by route
A visitor on the home page does not need the checkout code. Dynamic import() tells the bundler to put a module in its own file and load it on demand.
import { lazy, Suspense } from "react";
const Checkout = lazy(() => import("./pages/Checkout"));
export function App() {
return (
<Suspense fallback={<p>Loading…</p>}>
<Checkout />
</Suspense>
);
}
Frameworks such as Next.js split by route for you. In a custom setup, routes are the first place to add it.
3. Lazy load heavy features
Some components are large and rarely needed at first paint: charts, rich text editors, maps, image galleries. Load them when the user opens the tab or scrolls near them, not on page load.
On a shop, that means the gallery script loads on the product page and the payment scripts load on the checkout page. Nothing else pays for them.
4. Trim the dependencies
- Replace heavy libraries with lighter ones, or with the platform.
Intl.DateTimeFormatcovers many uses of a date library. - Import only what you use, and make sure it can be removed. See how tree shaking really works.
- Check the cost of a package before you add it.
5. Ship a real production build
- Minify the code and serve it with Brotli or gzip compression.
- Build in production mode so development warnings are stripped.
- Target modern browsers. Transpiling for very old ones adds helper code and polyfills that most visitors never need.
6. Keep it small
Bundles grow back one import at a time. Add a size budget to CI with a tool such as size-limit, so a pull request that adds 80 kB fails with a clear message instead of slipping through.
What to expect
Route splitting and one or two dependency swaps commonly cut the initial JavaScript by a third or more. To confirm the result on a real page, measure it with Chrome DevTools.