How TreeShaking unused components and code-splitting improved our WebApp performance by about 2000 ms
The myth of Tree-shaking and code-splitting at the bundler and browser level. A comprehensive guide to optimizing React bundles and reducing load times by 2 seconds.
Originally published on LinkedIn
View original post →How TreeShaking unused components and code-splitting improved our WebApp performance by about 2000 ms
Are your React bundles getting chunkier by the day? We faced the same issue until we implemented a simple yet powerful optimization strategy. And I'm going to unveil this sacred method to you in a bit, sit tight and enjoy the ride. :D
1. The Problem: Bloated Component Wrappers
The core issue we identified was in how our component wrappers were implemented. The traditional approach of static imports was silently bloating our bundle size, creating a direct trade-off between developer convenience and user experience—especially for those on unreliable connections or lower-tier devices TreeShaking.
An Instance for this look something like this:
import React from 'react';
import ComponentA from '@components/ComponentA';
import ComponentB from '@components/ComponentB';
export default function index({ template, ...props }) {
if (template == 'ComponentA') return <ComponentA {...props} />;
return <ComponentB {...props} />;
}
2. The Solution: Dynamic Imports + Code-Splitting
We refined our solution using template literals with dynamic imports, creating a more maintainable and scalable approach. By constructing the import path dynamically based on our theme configuration, we leverage both tree shaking at build time and efficient code splitting at runtime. The bundler analyzes the pattern and eliminates all unused Bar variants, while React's lazy() ensures only the required component loads when needed. This streamlined approach reduces code complexity while maintaining the performance benefits— visible in our bundle analyzer as optimized, tree-shaken chunks with clean separation.
import appThemeConfiguration from '@appConfig';
import React from 'react';
const template = appThemeConfiguration.BAR_VERSION;
const BarComponent = React.lazy(() =>
import(`./${template}Bar/${template}Bar.jsx`).then((module) => ({
default: module.default,
}))
);
export default function BarWrapper({ ...props }) {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<BarComponent {...props} />
</React.Suspense>
);
}
BarWrapper.displayName = 'BarWrapper';
3. Optimization Rollup Configuration
To maximize our optimization strategy, we implemented strategic code splitting through Rollup's manual chunk configuration. By categorizing dependencies into logical vendor groups—React, routing, state management—we ensure better caching and faster subsequent visits. While hashed filenames guarantee cache-busting on updates. This granular approach transforms our monolithic bundle into optimized, cache-friendly chunks that load in priority order, dramatically improving initial page load and runtime performance.
rollupOptions: {
output: {
// manualChunks,
// Optimize vendor code splitting for better caching
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('/react/') || id.includes('/react-dom/')) {
return 'vendor-react';
}
// React Router - routing library
if (id.includes('react-router')) {
return 'vendor-router';
}
// State management - Redux
if (
id.includes('@reduxjs/toolkit') ||
id.includes('react-redux')
) {
return 'vendor-redux';
}
}
},
entryFileNames: 'assets/[name]-[hash].js',
chunkFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash][extname]',
},
}
Key Takeaways
- Dynamic imports with React.lazy() enable true code-splitting at the component level
- Strategic chunking improves caching and reduces redundant downloads
- Bundle analysis helps identify optimization opportunities
- Performance gains of 2000ms directly impact user experience and Core Web Vitals
This optimization strategy transformed our application's performance, reducing bundle sizes and improving load times significantly. The combination of tree-shaking unused components and strategic code-splitting creates a more efficient, user-friendly web application.
Arsanyos Asrat
Frontend Engineer | Performance Optimization Specialist
