Vite 8 manualChunks: Migrate Rollup Splits to Rolldown codeSplitting
Vite 8 changes the bundler underneath the build from the older Rollup-oriented path to Rolldown. If your vite.config contains build.rollupOptions.output.manualChunks, migrate that split instead of assuming the old object syntax will keep working: Vite 8 no longer supports the object form, and the function form is deprecated.
The replacement is build.rolldownOptions.output.codeSplitting with explicit groups:
import { defineConfig } from 'vite';
export default defineConfig({ build: { rolldownOptions: { output: { codeSplitting: { groups: [ { test: /node_modules\/(react|react-dom)/, name: 'framework', }, ], }, }, }, },});The regex and chunk name are examples. Choose groups from the dependencies and loading behavior of your application, then verify the generated output rather than treating every old manualChunks map as a mechanical rename.
Map the old and new configuration boundaries
An older Vite configuration may look like this:
export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'], }, }, }, },});In Vite 8, move the underlying bundler options and express the split as a codeSplitting.groups rule:
export default defineConfig({ build: { rolldownOptions: { output: { codeSplitting: { groups: [ { test: /node_modules\//, name: 'vendor', }, ], }, }, }, },});The migration guide also lists build.rollupOptions as deprecated in favor of build.rolldownOptions. Update the outer option at the same time so a later Vite upgrade does not leave the old compatibility name scattered through the config.
Do not blindly split every dependency
Rolldown’s manual code-splitting documentation warns that moving modules between chunks can change execution behavior. A broad node_modules group is easy to write, but it can create a large vendor chunk, capture dependencies recursively, or alter the order in which modules initialize.
Use a staged migration:
- Record the old build’s chunk names and the routes that load them.
- Replace one
manualChunksrule with one narrowcodeSplittinggroup. - Run the production build and inspect
dist/assets. - Load the initial route and every route that uses a dynamic import.
- Check side effects, circular imports, and cache headers before adding another group.
Rolldown may also emit a runtime chunk when manual splitting needs a stable execution order. That is not automatically a regression. The question is whether the resulting application loads and initializes correctly under the deployment’s asset and cache rules.
Check for a silently lost lazy boundary
A successful build does not prove that a lazy boundary survived. An upstream Vite issue report for Vite 8.2.0 describes the function form of manualChunks being called while its assignments were silently discarded. In that reproduction, a shared CommonJS module returned to a lazy chunk, the entry still imported it statically, and the generated HTML preloaded the supposedly lazy chunk without a warning. The report measured the boot bundle growing from 978 KB to 1,085 KB gzip and all 275 assignments being ignored.
This is a bounded compatibility-path report, not proof that every Vite 8 project has the same failure. Verify the output that your application actually ships:
pnpm vite buildfind dist/assets -maxdepth 1 -type f | sortrg -n 'modulepreload' dist/index.htmlCompare the generated entry imports and modulepreload links with the route’s dynamic import. Use this decision table:
| Output check | Interpretation | Next move |
|---|---|---|
| The expected lazy route points to its own chunk and the entry does not preload it | The lazy boundary is present | Load the route in a fresh profile and check its request waterfall |
The entry imports or index.html preloads a chunk that should be lazy | The boundary may have been lost | Replace the compatibility manualChunks rule with native codeSplitting.groups, then inspect shared CommonJS and static imports |
| Chunk names changed but the expected lazy import remains lazy | The layout changed without proving a regression | Compare initial bytes, lazy-route bytes, cache headers, and initialization order |
Verify the build instead of the config alone
Run the same build command used by CI, then inspect both the build output and the application behavior:
pnpm vite buildfind dist/assets -maxdepth 1 -type f | sortFor a package script, use pnpm build instead. Check the generated HTML for the expected entry assets and modulepreload links, open a page that loads a lazy chunk, and test a fresh browser profile so a previously cached chunk does not hide a broken import.
If the project still targets an older Vite release, do not copy the Vite 8 configuration into it without checking that release’s supported options. Keep the migration in the same change as the Vite upgrade, or guard the configuration in a way the project can test in both environments.
FAQ
Q: Is manualChunks removed in Vite 8?
A: The object form of build.rollupOptions.output.manualChunks is no longer supported, and the function form is deprecated. Vite directs users to Rolldown’s codeSplitting option; an upstream Vite 8.2.0 report also shows why a successful compatibility-path build is not enough.
Q: How can I tell that Vite lost a lazy split?
A: Inspect the entry imports and modulepreload links in dist/index.html after a clean production build. If a chunk expected only behind a dynamic import is on the boot path, switch to native codeSplitting.groups and repeat the output and route checks.
Q: What replaces build.rollupOptions?
A: Use build.rolldownOptions for direct Rolldown configuration. Vite lists build.rollupOptions as a deprecated name during the Vite 8 migration.
Q: Will codeSplitting always improve performance?
A: No. It changes chunk boundaries, not a guaranteed performance outcome. Test initial loading, lazy routes, caching, and module initialization with the actual application and deployment headers.
References:
Rolldown manual code splitting
Vite issue: manualChunks assignments ignored in Vite 8.2.0 report
Report a typo or broken link, or suggest a related topic.