Definition
This is the concept you need to know to avoid the trap where, while trying to cache third-party library (node_modules) code shared by multiple pages into one bundle, you instead make every page download the entire giant shared bundle.
Let's unpack the terms first: a bundle is a single JS file webpack produces by merging multiple files; a chunk is a piece into which that bundle is split; a vendor chunk is a piece that gathers only external libraries; First Load is the total amount of JS you must receive when first opening a page; and dynamic import (import() / next/dynamic) is the technique of splitting code so it is "downloaded separately only when needed."
Concretely, if you give the catch-all vendor group in optimization.splitChunks.cacheGroups (the webpack setting that decides which modules go into which chunk) both a fixed name and chunks: "all", then even the modules you carefully split out with dynamic import get hoisted back into the initial shared vendor chunk, bloating the First Load of every page. In other words, dynamic import alone may not beat webpack's splitting policy.
Why It's Needed
Bundle optimization should first suspect the chunk shared by all entries before asking "what differs per route." If the First Load is nearly the same across routes, a shared vendor is likely the bottleneck. Even if you add an async cacheGroup (a grouping rule for async chunks), if that module still remains in a static import chain (a path where the page pulls it in directly with import from the start), it won't leave the initial graph.
How It Works
- webpack analyzes the module graph and groups modules matching a cacheGroup
testinto a chunk. - With
chunks: "all", modules in async chunks also become candidates for that group. - A fixed
name: "vendor"forces dependencies specific to multiple entries/routes into one file. - Even if you lazy-load a component with
next/dynamicetc., if the same module is referenced in two or more places, splitChunks may hoist it to shared. - A named async cacheGroup (
enforce: true) cannot pull out an import that is already in the initial graph — you have to make the import path itself async.
Practical Application
- Look at the list of modules inside vendor with
@next/bundle-analyzeror webpack-bundle-analyzer. The CLI's "shared by all" total alone is often insufficient. - Apply
chunks: "initial"to the catch-all vendor, remove the fixedname, and applymaxSize. - For heavy libraries (math, animation, canvas, etc.), use an async cacheGroup + dynamic import of the consuming component.
- Verify that the cacheGroup
testregex points at the actual package name that ends up in the bundle, not a wrapper package. - Verify the effect with a rebuild + gzip measurement for each change.
Tradeoffs
- Increasing vendor splitting can increase the number of shared chunks and HTTP requests. Balance between HTTP/2 / cache hits and the First Load target.
- Excessive cacheGroups raise build time and debugging complexity. For large chunks that are already lazy at the route level, additional splitting may have little payoff.
- Bundlers like Turbopack / Rspack have different default split policies — cross-check webpack custom settings against that bundler's docs.
When Not to Use It
- When the First Load is already small and differs greatly per route — a shared vendor may not be the main cause.
- Copy-pasting cacheGroups without measurement — if the environment / dependency graph differs, it can backfire.
- An attempt to unconditionally exclude all of node_modules from the initial vendor — risk of runtime chunk explosion and circular references.
Common Mistakes
- Assuming "it's separated" after adding dynamic import and not re-checking the analyzer.
- Adding only an async cacheGroup while keeping the static import above.
- Matching only
lottie-reactwhile leaving the actual heavy core package in vendor. - Falsely flagging completion from the
errorstring in build logs (which includes tool warning messages).
Related Concepts
- resource-priority — load priority / critical path
- lab-performance-measurement-variance — variance in bundle / metric measurement