How to Fix Vite Failed to Resolve Import Without Deleting node_modules on Repeat
A practical guide to fixing Vite failed to resolve import errors by checking the real import path, alias config, tsconfig paths, package exports, workspace links, and dependency optimization instead of reinstalling everything blindly.
Why this error keeps showing up: Vite is usually telling the truth. The import cannot be resolved from the file that asked for it, but the reason may be a typo, a missing alias, a package export boundary, or a workspace dependency that has not been built.
The error usually looks like this:
[plugin:vite:import-analysis] Failed to resolve import "@/components/Button"or:
Failed to resolve import "some-package/subpath" from "src/App.tsx"The tempting fix is to delete node_modules, reinstall, restart the dev server, and hope the machine feels more generous. Sometimes that works because the dependency cache was stale. Most of the time it just delays the real diagnosis.
Start with the exact import Vite is naming
Copy the failing import string and find where it appears:
rg '"@/components/Button"|some-package/subpath' srcThen check whether the target exists with the exact same spelling:
ls src/componentsThis sounds basic, but it catches a lot of failures:
Button.tsxwas renamed tobutton.tsx- the import points at
components/Buttonbut the file lives inui/Button - the folder exists locally on macOS but fails in CI because Linux file paths are case-sensitive
If the import path is wrong, fix that before touching config.
If you use @/, configure Vite, not just TypeScript
TypeScript path aliases help the editor and type checker understand imports. Vite still needs its own resolver config at runtime and build time.
A common alias setup looks like this:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'node:path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});If your tsconfig.json has this:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}but vite.config.ts does not have the matching resolve.alias, the app can look fine in the editor and still fail in Vite.
Restart Vite after changing resolver config
Resolver config is loaded when the dev server starts. After changing vite.config.ts, restart the server:
npm run devIf you are in a framework wrapper or a monorepo script, make sure the command you restarted is the one actually serving the failing app.
Check package subpaths and exports
This error also happens when the package exists but the subpath you imported is not public.
For example:
import thing from 'some-package/internal/thing';may fail even when some-package is installed. Modern packages can define an exports field that controls which entry points are available to consumers. If a subpath is not exported, Vite may refuse to resolve it.
Check the package metadata:
node -p "require('./node_modules/some-package/package.json').exports"Then import from a documented entry point instead of copying an internal path from a blog post, Stack Overflow answer, or compiled output.
Verify the dependency is installed where this app runs
In monorepos, the dependency may be installed at the root while the app package does not declare it. That can work accidentally in one setup and fail in another.
From the app directory, run:
npm ls some-packageor, with pnpm:
pnpm why some-packageIf the app imports the package directly, the app package should usually declare the dependency directly. Do not rely on a sibling package or hoisting layout to make imports available.
Watch for workspace packages that need a build
If the import points to a local workspace package, Vite may be resolving source or built output depending on that package's package.json.
Check these fields:
{
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts"
}If the package points at dist but dist does not exist, Vite is not confused. The package has not been built.
Run the workspace build:
npm run build --workspace @acme/uior change the package to expose source intentionally during local development if that is how your repo is designed.
Use dependency optimization only for the right problem
Sometimes a dependency is present but Vite has trouble pre-bundling or detecting it. In that case, optimizeDeps.include can help:
// vite.config.ts
export default defineConfig({
optimizeDeps: {
include: ['some-package'],
},
});Use this after you have confirmed the package exists and the import path is valid. It should not be the first fix for a typo, a missing alias, or a package that does not export the path you are importing.
Clear Vite cache only after the config is correct
If the import path, alias, package export, and dependency declaration all look correct, then clear Vite's cached dependency metadata:
rm -rf node_modules/.vite
npm run dev -- --forceFor pnpm or yarn projects, use the package manager command your repo already uses. The important part is that cache clearing comes after diagnosis, not before it.
A fast checklist
Use this order:
- confirm the file or package exists
- check exact case-sensitive spelling
- make Vite
resolve.aliasmatch TypeScriptpaths - restart the Vite dev server
- check package
exportsbefore importing subpaths - declare dependencies in the app that imports them
- build local workspace packages if their entry points target
dist - clear Vite cache only when the real configuration already makes sense
Bottom line
Failed to resolve import is not a reinstall problem by default. Treat it as a resolver question: where is this import coming from, what should it point to, and which tool knows that mapping? Once the path, alias, and package boundary agree, Vite usually stops complaining without a full dependency ritual.