React Bundle Size Audit Checklist Before You Ship to Production
A practical React bundle size audit checklist covering analysis tools, common bloat sources, tree-shaking issues, and quick wins before every production release.
A bundle audit shows which code and assets users download for each route. Start with a production bundle analysis, then investigate large dependencies, code splitting, tree shaking, and build settings.
Before You Start: Generate a Bundle Analysis
Generate a visual bundle analysis of the production build.
- Vite: Install
rollup-plugin-visualizer. Add it tovite.config.jsplugins. Runvite build. Open the generatedstats.html. - Webpack: Configure
webpack-bundle-analyzeror generate a stats JSON file with your build command, then pass its path to the analyzer. Create React App may require a different tool or configuration; do not assume it forwards webpack CLI flags. - Next.js: Use
@next/bundle-analyzer. Enable it innext.config.jsand runANALYZE=true npm run build.
Keep the treemap open as you work through the checklist. Compare route-level downloads and runtime behavior after each change; some changes move code between chunks rather than reduce total size.
Dependencies and Third-Party Libraries
- Check for duplicate packages. Run
npm ls <package-name>for major libraries. Multiple versions of React, lodash, or date libraries in the same bundle are a common problem in monorepos and projects with many dependencies. - Review Moment.js usage. If date handling contributes substantially to the bundle, compare a smaller library or native APIs. Check locale, timezone, and formatting behavior before replacing it.
- Audit icon libraries. Check which icons survive in the production bundle. Use the package’s documented import paths; an index import does not necessarily include every icon when tree shaking works.
- Check lodash usage.
import _ from "lodash"imports the entire library. Useimport debounce from "lodash/debounce"or switch to native equivalents for simple utilities. - Look for libraries with lighter alternatives. A full charting library may be 200KB when only one chart type is needed. A date picker library may include internationalization data for every locale.
- Verify peer dependencies are not duplicated. Some dependencies bundle their own copy of React or other shared libraries. Check the bundle map for unexpected duplicates.
Tree Shaking
- Check whether unused exports are removed. Static ES module imports and exports support tree shaking. Namespace imports can also be optimized when usage is analyzable; dynamic property access and module side effects can limit removal.
- Check
sideEffectsconfiguration. Inpackage.json, thesideEffectsfield tells bundlers which files can be safely dropped if their exports are unused. Confirm your own packages and any in-house libraries have this set correctly. - Inspect barrel files when the bundle includes unexpected modules. Re-exports can be tree-shaken, but side effects and package metadata affect the result. Compare direct imports only after identifying code that remains unnecessarily.
Code Splitting
- Confirm route splitting works. Use your router or framework’s splitting mechanism; frameworks such as Next.js handle route splitting without requiring
React.lazyon every route. Account for prefetching and cached chunks when inspecting network requests. - Identify heavy components that are conditionally rendered. Modals, drawers, editors, and admin panels that appear on interaction are good candidates for splitting, especially if they import large libraries.
- Verify loading and error recovery. A suitable ancestor
Suspenseboundary can cover multiple lazy components. Test how the application handles chunk-load failures.
Assets and Static Files
- Check for uncompressed images imported directly into JavaScript. SVGs imported as React components are fine for small icons but can inflate the bundle if used for large illustrations.
- Confirm CSS is not inlined in JS unnecessarily. CSS-in-JS solutions that serialize styles at render time can add to JavaScript execution cost. Verify this is intentional.
- Check font loading strategy. Fonts loaded via JavaScript rather than CSS or
<link>tags can block rendering and inflate the JS parse cost.
Build Configuration
- Verify production mode is active. A React build with
NODE_ENV=developmentincludes PropTypes, development warnings, and extra checks. Confirmprocess.env.NODE_ENVis set to"production"in the production build. - Confirm minification is running. Check that the output files are minified. A Vite or Webpack production build should produce compressed output by default, but configuration overrides can disable this.
- Enable compression. Static assets should be served with gzip or Brotli compression. This is a server or CDN configuration, not a bundler setting, and should be measured alongside bundle changes.
- Check chunk splitting strategy. Vendor chunk behavior depends on the bundler, version, and configuration. Review whether shared chunks reduce repeated downloads without making the initial route load unnecessarily large.
After the Audit
- Record the baseline bundle sizes before starting. Compare main chunk size and total transfer size after each fix.
- Run Lighthouse in incognito on a throttled connection. Measure the impact on transfer size and execution time on a representative mobile device and connection.
- Set a bundle size budget. Most bundlers support size limits in configuration. Failing the build when a chunk exceeds a threshold prevents regressions from accumulating silently.
A bundle audit is most useful as a recurring practice — run it before major releases and any time a new heavy dependency is added to the project.