Most 'works after restart' and 'duplicate React' dev issues trace back to dependency optimization.
On the first dev-server start, Vite scans your source for bare imports (import { debounce } from "lodash-es") and pre-bundles those dependencies (using Rolldown in Vite 8) into node_modules/.vite/deps. Two reasons:
- CommonJS and UMD to ESM: many packages still ship CommonJS, which browsers cannot import natively.
- Fewer requests: a package made of hundreds of internal modules becomes one file, avoiding request waterfalls.
The browser then receives cached, strongly cached (max-age=31536000,immutable) files, and Vite rewrites your imports to point at them.
The cache is invalidated when the lockfile, the relevant config or patchedDependencies change. Force a rebuild with vite --force, or delete node_modules/.vite.
When something goes wrong:
- Dependency discovered late: an import that only appears after navigation (e.g. inside a lazily loaded route) triggers re-optimization and a page reload. Add it to
optimizeDeps.include. - Linked monorepo packages are treated as source, not pre-bundled. If such a package ships CommonJS, add it to
include. - Large ESM-only packages that are already browser-ready can go in
optimizeDeps.exclude. - Duplicated singletons (two copies of React) usually need
resolve.dedupe: ["react", "react-dom"].
Pre-bundling applies only to the dev server; production builds bundle dependencies along with your code.
Example
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
optimizeDeps: {
include: [
"lodash-es", // discovered late (only in a lazy route)
"@acme/legacy-utils", // linked workspace package that ships CommonJS
],
exclude: ["@acme/esm-widgets"], // already ESM, keep as source
},
resolve: {
dedupe: ["react", "react-dom"], // one copy even if several are installed
},
});
// $ npx vite --force # rebuild the dependency cache
// $ ls node_modules/.vite/deps # pre-bundled files + _metadata.jsonRemember
- Pre-bundling converts CommonJS to ESM and collapses many files
- Cache lives in node_modules/.vite;
--forcerebuilds it includelate or linked CJS deps;excludebrowser-ready ESM- Use
resolve.dedupefor duplicated singletons
Watch out for
- Repeatedly deleting node_modules instead of adding a late-discovered dependency to
optimizeDeps.include.