Patterns
Code Splitting Strategies
Optimize bundle size and improve Core Web Vitals with smart code splitting
Code Splitting Strategies
Code splitting is one of the most effective ways to improve your app's performance and Core Web Vitals. Load only what users need, when they need it.
🎯 Why Code Splitting?
Without Code Splitting:
Bundle: 2.5 MB
FCP: 4.2s ❌
LCP: 5.8s ❌
User waits forever...
With Code Splitting:
Initial: 150 KB
FCP: 0.8s ✅
LCP: 1.2s ✅
Rest loads as needed📊 Impact on Core Web Vitals
| Metric | Without Splitting | With Splitting | Improvement |
|---|---|---|---|
| FCP | 4.2s | 0.8s | 81% faster |
| LCP | 5.8s | 1.2s | 79% faster |
| TTI | 6.5s | 1.5s | 77% faster |
| Bundle | 2.5 MB | 150 KB | 94% smaller |
🔧 Code Splitting Methods
1. Route-Based Splitting (Most Common)
// ❌ BAD: All routes loaded upfront
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
import Settings from './pages/Settings';
import Profile from './pages/Profile';
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</Routes>
);
}
// ✅ GOOD: Each route loads on demand
import { lazy, Suspense } from 'react';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const Profile = lazy(() => import('./pages/Profile'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
);
}2. Component-Based Splitting
// ❌ BAD: Heavy component always loaded
import RichTextEditor from './RichTextEditor'; // 500 KB!
function BlogPost() {
const [editing, setEditing] = useState(false);
return (
<div>
{editing && <RichTextEditor />}
</div>
);
}
// ✅ GOOD: Load only when needed
const RichTextEditor = lazy(() => import('./RichTextEditor'));
function BlogPost() {
const [editing, setEditing] = useState(false);
return (
<div>
{editing && (
<Suspense fallback={<div>Loading editor...</div>}>
<RichTextEditor />
</Suspense>
)}
</div>
);
}3. Library Splitting (Vendor Chunks)
// next.config.js / vite.config.js
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
// Vendor chunk for React
'react-vendor': ['react', 'react-dom'],
// Heavy libraries separate
'charts': ['chart.js', 'react-chartjs-2'],
'editor': ['slate', 'slate-react'],
// Utils together
'utils': ['lodash', 'date-fns']
}
}
}
}
};📈 Bundle Analysis
# Analyze your bundle
npm run build
npx source-map-explorer 'dist/*.js'
# Or with webpack
npx webpack-bundle-analyzer stats.jsonWhat to look for:
- ✅ Main bundle < 200 KB (gzipped)
- ✅ Vendor chunks < 150 KB each
- ✅ Route chunks < 100 KB each
- ⚠️ Any chunk > 500 KB needs splitting
🎯 Code Splitting Checklist
- Routes are lazy loaded
- Heavy components split (charts, editors, etc)
- Vendor libraries in separate chunks
- Dynamic imports for conditional features
- Preload critical routes
- Bundle analyzed and optimized
- Core Web Vitals tested
📚 Pattern Catalog
This section covers:
- Route-Based Splitting - Lazy load routes (most impactful)
- Component-Based Splitting - Dynamic imports for heavy components
- Library Splitting - Vendor chunks optimization
- CSS Splitting - Critical CSS extraction
- Code Splitting SSR - Server-side considerations
- Preload Strategies - Intelligent preloading
🏆 Best Practices
- Measure first - Use Lighthouse/WebPageTest
- Split at routes - Easiest and most effective
- Target 200 KB initial - Gzipped bundle size
- Preload critical paths - User likely navigation
- Monitor over time - Bundle size grows quickly
- Test on 3G - Slow networks expose issues
- Use CDN - Faster chunk delivery
🎓 Quick Wins
// 1. Lazy load routes (5 min, huge impact)
const Dashboard = lazy(() => import('./Dashboard'));
// 2. Split heavy libraries (10 min)
const Chart = lazy(() => import('react-chartjs-2'));
// 3. Vendor chunks (webpack.config.js, 5 min)
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /node_modules/,
name: 'vendor'
}
}
}
}
// Result: 70-80% smaller initial bundle ✅📊 Real-World Results
Before Code Splitting
Initial Bundle: 2.3 MB
FCP: 4.1s
LCP: 5.9s
Lighthouse Score: 34After Code Splitting
Initial Bundle: 178 KB
FCP: 0.9s
LCP: 1.3s
Lighthouse Score: 96Improvement: 92% smaller bundle, 78% faster load time 🚀
Next: Dive into specific splitting strategies to optimize your app for Core Web Vitals.