Choosing a Webpack 5 splitChunks Strategy for Production Bundles
A decision guide for Webpack 5 splitChunks: compare default, vendor-only, and custom cache group strategies with a production-ready configuration, validation commands, and caching verification steps.
10 Apr 2026, 07:00 UTC

The Decision You're Facing
Webpack 5 enables splitChunks by default in production mode, but the zero-config behavior rarely matches real-world caching and bundle-size goals. You need to decide whether the default splitting, a simple vendor isolation, or custom cache groups best serves your application's dependency update frequency, HTTP protocol, and team maintenance capacity. The wrong choice means either bloated vendor chunks that invalidate on every deploy or dozens of tiny files that choke HTTP/1.1 connections.
Constraints That Shape the Choice
- Webpack 5+ in production mode (
mode: 'production'orNODE_ENV=production) - HTTP/2 (multiplexing) or HTTP/1.1 (connection limits) delivery
- Dependency update cadence: daily, weekly, or monthly
- Team tolerance for configuration maintenance
Strategy Comparison
| Strategy | Config Effort | Granularity | Best For | Risk |
|---|---|---|---|---|
| Default (zero config) | None | Medium — splits chunks >30KB, min 1 reference | Small apps, HTTP/2, infrequent deploys | Many small chunks on HTTP/1.1; vendor hash changes on any dep update |
| Vendor-only split | Low — single cacheGroup | High — all node_modules in one chunk | Stable dependencies, HTTP/1.1, long-term caching priority | Single vendor chunk invalidates on any dependency change |
| Custom cacheGroups | Medium — multiple named groups with priority | Very high — per-package or per-category | Large apps, mixed update frequencies, HTTP/2 | Config drift; over-splitting creates too many requests |
| Disabled | Explicit splitChunks: false | None — single bundle | Micro frontends, Module Federation remotes, tiny apps | No caching granularity; full bundle re-download on any change |
Trade-offs in Practice
Default Splitting
Webpack's built-in heuristics create chunks when a module is shared across multiple entry points or exceeds 30KB. On HTTP/2 this works well — multiplexing absorbs the request overhead. On HTTP/1.1, you may see 20–40 chunk requests, each incurring latency. The vendor chunk hash changes whenever any dependency updates, forcing clients to re-download React, Lodash, and your internal utils together.
Vendor-Only Isolation
A single vendor cache group matching /[\\/]node_modules[\\/]/ pulls all third-party code into one file. This maximizes cache lifetime if your dependencies rarely change. The moment you bump a patch version of date-fns, the entire vendor chunk gets a new content hash. Use this when your package-lock.json stays stable for weeks.
Custom Cache Groups
Separate high-churn from low-churn packages. React and React-DOM update on a predictable schedule — isolate them. Heavy visualization libraries (Chart.js, Three.js, pdfmake) change infrequently but are large — give them their own chunk. A catch-all vendor group with reuseExistingChunk: true handles the rest. Priority ordering ensures specific groups match first.
Concrete Implementation: Custom Cache Groups
Place this in your production webpack config (webpack.prod.js). Requires write access to the config file and Node.js 18+ with Webpack 5 installed.
module.exports = {
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
reactVendor: {
test: /[\\/]node_modules[\\/] (react|react-dom)[\\/]/,
name: 'react-vendor',
chunks: 'all',
priority: 20
},
heavyLibs: {
test: /[\\/]node_modules[\\/] (chartjs|three|pdfmake)[\\/]/,
name: 'heavy-libs',
chunks: 'all',
priority: 10
},
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 5,
reuseExistingChunk: true
}
}
}
}
};
Key points: priority ensures specific groups match before the catch-all. reuseExistingChunk: true prevents a module already placed in react-vendor from being duplicated in vendors. runtimeChunk: 'single' extracts the webpack runtime into its own file so vendor hashes stay stable when entry points change.
Validation Steps
- Produce stats: Run in your project root with production env:
Requires execute permission onNODE_ENV=production npx webpack --config webpack.prod.js --json > stats.jsonnode_modules/.bin/webpack. Outputsstats.jsonin the current directory. - Visualize chunks:
Opensnpx webpack-bundle-analyzer stats.json --mode static --report report.htmlreport.htmlin your browser. Look for: chunk count, vendor chunk sizes, and whetherreact-vendorandheavy-libsappear as separate blocks. - Verify hash stability: Build twice without changing dependencies. Compare
dist/vendors.[contenthash].jsfilenames — they should be identical. If they differ, something in the chunk graph is non-deterministic (often a timestamp or non-relative import). - Check cache headers on staging: Vendor chunks must serve with
Cache-Control: max-age=31536000, immutable. Usecurl -I https://staging.example.com/vendors.abc123.jsto verify. This requires access to staging deployment config (CDN, nginx, Apache). - Diff between commits: Install
stats-webpack-pluginand comparestats.jsonfrom two commits to confirm only expected chunks changed.
Limitations and Gotchas
- Development mode:
splitChunksis disabled by default inmode: 'development'. Enable explicitly if you need parity. - CSS extraction:
MiniCssExtractPlugincreates separate CSS chunks.splitChunksdoesn't affect them — add astylescacheGroup if you want CSS splitting. - Tree shaking dependency: If
sideEffects: falseisn't honored inpackage.json, vendor chunks may include dead code. Verify withwebpack-bundle-analyzer— look for unused exports in vendor chunks. - Module Federation: Remote entries have independent caching.
splitChunksonly applies to local bundles; shared dependencies are negotiated viasharedconfig. - HTTP/1.1 tuning: If you're on HTTP/1.1, increase
minSize(default 30KB) to 50–100KB andminChunksto 2–3 to reduce request count.
How to Know It's Working
After deploying, monitor two metrics for 1–2 weeks: (1) percentage of returning visitors receiving 304 Not Modified for vendor chunks (should exceed 90% on stable weeks), and (2) total JS bytes transferred per session (should drop 20–40% versus a single-bundle baseline). If vendor hashes change on every deploy despite no dependency updates, re-run the stats diff to find the leaking module.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.