Vite optimize deps — Missing Exports and Reload Loops
Diagnose Vite dependency pre-bundling problems by clearing the cache, reading optimize debug output, and configuring include, exclude, and dedupe for linked packages.
Symptoms
- The requested module '/node_modules/.vite/deps/chunk-XXXX.js' does not provide an export named 'default'
- The browser reloads repeatedly with new dependencies optimized
- A workspace package resolves to a stale or duplicate copy after an edit
- CommonJS dependencies fail only in dev, while the production build works
Possible causes
- The pre-bundled cache is stale after a dependency upgrade or lockfile change
- A CommonJS package is imported by name but never pre-bundled
- A linked workspace package is pre-bundled and keeps its own copy of a shared framework
- The dependency is discovered late through a dynamic import, triggering re-optimization
Diagnosis steps
Clear the pre-bundle cache and restart with force
rm -rf node_modules/.vite && npx vite --forceInterpretation
A clean start removes stale hashed chunks. If the error disappears, the cache was the trigger and the configuration still deserves review.
Read the optimizer debug output
npx vite --debug 2>&1 | grep -E 'optimiz|deps|reload' | head -40Interpretation
The log lists which dependencies were discovered, bundled, and why a reload was triggered. A dependency appearing repeatedly points to late discovery.
Check how the import is resolved
npx vite --debug 2>&1 | grep -i 'lodash-es\|some-cjs-lib' | head -20Interpretation
The resolved path shows whether the import comes from the workspace or node_modules and whether it is treated as an external or bundled dependency.
Inspect duplicate framework copies
npm ls react react-dom && ls node_modules/.vite/deps | head -20Interpretation
Two React versions in the tree cause invalid hooks and duplicate context. The deps directory shows what was pre-bundled and under which hash.
Root cause
Vite pre-bundles dependencies into ESM files under node_modules/.vite so the browser does not request hundreds of modules. The cache is keyed by config, lockfile, and discovered entries; a change invalidates it, and a dependency discovered only at runtime forces a re-optimization and page reload. Linked packages that get bundled can carry their own framework copy, which breaks singleton assumptions and named exports.
Fix
Clear node_modules/.vite after dependency changes, add CommonJS or deep-import dependencies to optimizeDeps.include, exclude linked workspace packages so Vite serves their source directly, and dedupe shared frameworks. Use server.warmup to pre-transform entry points and avoid the first-load reload loop.
Risk note
Excluding a CommonJS package from optimization can make it fail in the browser because it is not valid ESM. Duplicating a framework through a linked package causes subtle runtime errors even when the build succeeds. Deleting the cache on every start hides configuration problems and slows development.
Verify the fix
Restart Vite, confirm the debug log shows one optimization pass with no subsequent reload, and verify the failing named import resolves in the browser. HMR should update the linked package without restarting the server.
Dev serves ESM, production bundles
Vite pre-bundles dependencies in development so the browser gets one ESM file per package instead of hundreds of requests. That cache lives under node_modules/.vite and is keyed by config and lockfile. When it is stale, imports point at old hashed chunks:
The requested module '/node_modules/.vite/deps/chunk-4F2K.js?v=9a1c'
does not provide an export named 'default'
Reset and observe
rm -rf node_modules/.vite
npx vite --force
npx vite --debug 2>&1 | grep -E "optimiz|deps|reload" | head -40
The debug output shows discovery, bundling, and reload events. A dependency that appears in a second optimization after startup was discovered late, usually through a dynamic import or a plugin transform.
Configure the optimizer
// vite.config.ts
export default defineConfig({
optimizeDeps: {
include: ['lodash-es', 'some-cjs-lib'],
exclude: ['@acme/ui'],
},
resolve: {
dedupe: ['react', 'react-dom'],
},
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/*.tsx'],
},
},
});
includeforces pre-bundling for dependencies Vite cannot discover statically.excludekeeps a linked workspace package as source, so its edits hot-reload and it does not carry a second framework copy.dedupeensures one React instance across the app and linked packages.
Verify
Restart and confirm the optimizer runs once, the page does not reload in a loop, and the previously failing import resolves. Edit a file in the linked package and check HMR applies without a full restart. Finally, run the production build; dev-only optimizer fixes should not change runtime behavior, and a dependency that breaks only in build needs a different fix.