If your React app feels sluggish, your Lighthouse score is bleeding red, and your users are bouncing before the first paint, chances are your JavaScript bundle is the culprit. A bloated bundle hurts Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and ultimately your conversion rate.
At Box Software, we ship React applications every week, and we’ve spent years fine-tuning the process of trimming bundles down. In this guide, we share 8 actionable techniques you can apply today to reduce bundle size in React and get back to lightning-fast load times.
Why Bundle Size Matters More Than Ever in 2026
Google’s Core Web Vitals are now a confirmed ranking factor, and INP replaced FID as the official responsiveness metric. With mobile traffic dominating and 5G adoption still uneven worldwide, every kilobyte you save translates into:
- Faster Time to Interactive (TTI)
- Better SEO rankings thanks to improved Core Web Vitals
- Lower bounce rates on mobile and low-end devices
- Reduced bandwidth costs on CDN and hosting
A React app shipping a 2MB JavaScript bundle on a mid-range Android phone over 4G can take 8+ seconds to become interactive. That’s unacceptable in 2026.

1. Analyze Before You Optimize: Use webpack-bundle-analyzer
You cannot fix what you cannot see. The first step is always measurement. This write-up is worth a look.
Install the analyzer:
npm install --save-dev webpack-bundle-analyzer
Then add it to your Webpack config:
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
plugins: [new BundleAnalyzerPlugin()]
};
If you use Vite, the equivalent is rollup-plugin-visualizer. For Next.js, use @next/bundle-analyzer. We break it down further here.
You’ll get an interactive treemap showing exactly which modules are eating your bundle. Look for:
- Duplicated dependencies (multiple versions of the same library)
- Heavy libraries that could be replaced
- Polyfills you no longer need
- Entire libraries imported when you only use one function
2. Implement Code Splitting with React.lazy and Suspense
Instead of shipping one giant JavaScript file, split your app into chunks that load on demand. React makes this trivial with React.lazy.
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
Route-based code splitting alone can cut your initial bundle by 40 to 60%.
3. Leverage Tree Shaking Properly
Tree shaking removes unused exports from your final bundle, but it only works under specific conditions: There’s a good explainer over at github.com.
- Use ES modules (import/export), not CommonJS (require)
- Set
"sideEffects": falsein your package.json when applicable - Avoid importing entire libraries
Bad practice:
import _ from 'lodash';
_.debounce(fn, 300);
Good practice:
import debounce from 'lodash/debounce';
debounce(fn, 300);
Even better, switch to lodash-es which is fully tree-shakeable, or replace lodash entirely with native ES features.

4. Replace Heavy Libraries with Lighter Alternatives
Many popular libraries have a smaller, modern equivalent. Here’s a comparison of common swaps:
| Heavy Library | Lighter Alternative | Size Saving |
|---|---|---|
| Moment.js (70KB) | date-fns or Day.js | ~60KB |
| Lodash (full) | lodash-es or native ES | ~60KB |
| Axios | Native fetch + ky | ~15KB |
| Redux + Redux Toolkit | Zustand or Jotai | ~20KB |
| Formik | React Hook Form | ~10KB |
5. Use Dynamic Imports for Conditional Features
Not every feature needs to be in the main bundle. Chart libraries, PDF generators, rich text editors, and emoji pickers should be loaded only when the user actually triggers them.
async function handleExportPDF() {
const { jsPDF } = await import('jspdf');
const doc = new jsPDF();
doc.text('Hello world', 10, 10);
doc.save('a4.pdf');
}
The user only downloads the 200KB PDF library when they click “Export”, not on initial page load.
6. Lazy Load Images and Components Below the Fold
Combine native lazy loading for images with React component lazy loading:
<img src="hero.webp" loading="lazy" alt="..." />
For React components, libraries like react-intersection-observer let you mount components only when they enter the viewport. This is particularly impactful on long landing pages and dashboards with multiple widgets.
7. Enable Minification, Compression, and Modern Output
This sounds basic, but we still audit production apps in 2026 where compression isn’t enabled. Make sure your stack delivers:
- Minification via Terser (default in Webpack 5 production mode)
- Gzip or Brotli compression at the server or CDN level (Brotli typically gives 15-20% better compression than Gzip)
- Modern JavaScript output: target ES2020+ for modern browsers and serve legacy bundles via differential loading only when needed
In Vite, this is mostly automatic. In Webpack, ensure mode: 'production' is set.

8. Remove Unused Dependencies and Dead Code
Run a quick audit:
npx depcheck
This tool lists unused dependencies sitting in your package.json. Combine it with ESLint rules like no-unused-vars and import/no-unused-modules to catch dead code before it reaches production.
Also consider:
- Removing legacy polyfills if you no longer support old browsers
- Auditing your CSS-in-JS solution (styled-components and Emotion add runtime overhead, modern alternatives like vanilla-extract or Tailwind have zero runtime)
- Reviewing your i18n strategy: load only the active locale, not all translations
Bonus: Quick Checklist Before Going to Production
- Run
webpack-bundle-analyzeron every release - Set a performance budget in your bundler config (fail the build if the bundle exceeds X KB)
- Monitor real user metrics with tools like Sentry, Datadog, or Vercel Analytics
- Use
preloadandprefetchhints for critical chunks - Test on a throttled 4G connection and mid-tier device, not just your MacBook Pro
Real-World Impact
On a recent client project at Box Software, applying these 8 techniques took the initial bundle from 1.8MB down to 420KB (gzipped: 140KB). LCP went from 4.2 seconds to 1.3 seconds on mobile, and the Lighthouse performance score jumped from 47 to 96.
That kind of improvement is not just technical satisfaction. It directly impacts SEO rankings, conversion rates, and user retention.
FAQ: Reducing React Bundle Size
Does React.lazy actually reduce bundle size?
Yes. React.lazy splits your code into separate chunks that are loaded on demand. While the total amount of JavaScript shipped over time may be similar, the initial bundle downloaded on first visit is dramatically smaller, which is what matters for Core Web Vitals.
How do I check the bundle size in a Vite React project?
Install rollup-plugin-visualizer, add it to your Vite config, and run vite build. A stats.html file will be generated showing an interactive treemap of your bundle.
What is a good bundle size for a React app?
Aim for an initial JavaScript bundle under 170KB gzipped for the main route. Google recommends this threshold to ensure good performance on mid-range mobile devices over 4G.
Is tree shaking automatic in Webpack 5?
Tree shaking is enabled by default in production mode, but it only works if you use ES modules and your dependencies are properly marked with sideEffects in their package.json. Always verify with the bundle analyzer.
Should I switch from Webpack to Vite to reduce bundle size?
Vite typically produces slightly smaller bundles thanks to Rollup’s better tree shaking and modern defaults, and the developer experience is much faster. If you’re starting a new project in 2026, Vite or Next.js are excellent choices. For existing Webpack apps, the migration cost should be weighed against the gains.
Need Help Optimizing Your React App?
At Box Software, we help companies audit, refactor, and optimize their React applications for peak performance. If your app is slow, your Core Web Vitals are red, or you simply want a technical second opinion, get in touch with our team. We’ll deliver a bundle audit and an actionable plan to make your app blazing fast.
