Vue/Vite: @ alias fails to resolve
Fix Vite and Vue "Failed to resolve import" for the @ alias by configuring resolve.alias and matching tsconfig paths.
Symptoms
- Failed to resolve import "@/components/Foo.vue"
- Imports with @ work in the editor but fail at build time
- The alias resolves in dev but not in the production build
Possible causes
- resolve.alias is not configured in vite.config
- tsconfig paths are set but Vite does not read them
- The alias is configured but points at the wrong directory
- A plugin or framework reset the resolve configuration
Diagnosis steps
Check vite.config for a resolve.alias entry
Interpretation
Vite does not know @ by default; without an explicit alias it treats @ as a package name.
Verify the alias target resolves to the src directory
Interpretation
A wrong base path produces "failed to resolve" even though the alias exists.
Confirm tsconfig paths match the alias
Interpretation
Mismatched paths make the editor and the bundler disagree, so one passes while the other fails.
Root cause
Bundlers resolve module specifiers literally unless an alias is declared. The @ prefix is a convention, not a built-in, so Vite fails to resolve it without resolve.alias. Editors often read tsconfig paths separately, which is why the import can appear valid in the IDE while the build rejects it.
Fix
Add resolve.alias in vite.config mapping @ to the src path using fileURLToPath or path.resolve, and mirror the same mapping in tsconfig paths so the editor agrees. Also configure the test runner and any type-check step to use the alias, since each tool resolves independently.
Verify the fix
Run the dev server and a production build; both resolve @ imports without error, and the editor no longer shows unresolved-import warnings.
Symptoms
The build fails with Failed to resolve import "@/components/Foo.vue". The same import may look fine in the editor, so the two tools disagree.
Diagnosis
Inspect the Vite config and tsconfig:
// vite.config.js — check for resolve.alias
import { fileURLToPath, URL } from 'node:url';
export default defineConfig({
resolve: {
alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },
},
});
If there is no alias, Vite treats @ as a package scope.
Fix
Configure the alias and mirror it for the editor:
// tsconfig.json
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] } } }
Verification
Run vite dev and vite build; both resolve @/... imports with no error, and the editor no longer underlines them, confirming every tool uses the same mapping.
Additional Notes
Editors and bundlers resolve module specifiers independently, so an alias that satisfies one does not automatically satisfy the other, which is why a path can look valid in the ide yet fail the build. Declaring the alias in the bundler and mirroring it in the type checker keeps both views consistent, and a wrong target directory produces the same unresolved message as a missing alias. Because wrappers such as framework plugins can reset the resolve configuration, reviewing the effective settings rather than assuming defaults is the reliable way to confirm where resolution broke.