Fix: ESLint import/no-unresolved Error (Module Exists but ESLint Can't Find It)
Quick Answer
How to fix ESLint's import/no-unresolved errors when modules actually resolve correctly — configure eslint-import-resolver-typescript, fix path alias settings, and handle node_modules that ESLint cannot find.
The Error
ESLint reports import errors even though the imports work correctly in the application:
error Unable to resolve path to module '@/components/Button' import/no-unresolved
error Unable to resolve path to module '../utils/helpers' import/no-unresolved
error Unable to resolve path to module 'some-package' import/no-unresolvedOr TypeScript path aliases are not recognized:
error Unable to resolve path to module '@components/Header' import/no-unresolved
error Unable to resolve path to module '~/utils' import/no-unresolvedOr after adding a new package:
error Unable to resolve path to module 'date-fns' import/no-unresolvedThe build and runtime work fine — only ESLint reports the error.
Why This Happens
The import/no-unresolved rule comes from eslint-plugin-import. It resolves module paths using its own resolver — separate from TypeScript’s module resolution, webpack’s resolve, or Node.js’s require(). By default it uses Node.js resolution rules and does not understand:
- TypeScript path aliases (
@/,~/,@components/) defined intsconfig.json - webpack aliases defined in
webpack.config.js - Vite aliases defined in
vite.config.js - Packages with only type definitions or non-standard
exportsfields - Monorepo packages linked via workspaces
The fix is to install and configure a resolver that matches how your bundler or TypeScript resolves modules.
Fix 1: Install and Configure eslint-import-resolver-typescript
For TypeScript projects, this is the standard fix — it makes eslint-plugin-import use TypeScript’s module resolution:
npm install --save-dev eslint-import-resolver-typescriptESLint flat config (eslint.config.js — ESLint 9+):
import importPlugin from 'eslint-plugin-import';
import tsParser from '@typescript-eslint/parser';
export default [
{
plugins: { import: importPlugin },
languageOptions: {
parser: tsParser,
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true, // Always try @types/* packages
project: './tsconfig.json',
},
},
},
rules: {
'import/no-unresolved': 'error',
},
},
];Legacy .eslintrc.js format:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['import'],
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
rules: {
'import/no-unresolved': 'error',
},
};For monorepos with multiple tsconfig files:
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: [
'./tsconfig.json',
'./packages/*/tsconfig.json',
'./apps/*/tsconfig.json',
],
},
},
},Fix 2: Configure Path Aliases Correctly in tsconfig.json
The resolver reads path aliases from tsconfig.json. Ensure they are defined there — not just in Vite or webpack config:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"@hooks/*": ["src/hooks/*"],
"~/*": ["src/*"]
}
}
}After updating tsconfig.json, restart your ESLint server (in VS Code: Ctrl+Shift+P → “ESLint: Restart ESLint Server”).
Verify the alias resolves correctly:
# Use eslint with --debug to see resolver output
npx eslint --debug src/App.tsx 2>&1 | grep -i "resolver\|alias\|resolve"Fix 3: Use eslint-import-resolver-alias for Non-TypeScript Projects
For JavaScript projects using webpack or Vite with path aliases:
npm install --save-dev eslint-import-resolver-alias// .eslintrc.js
module.exports = {
settings: {
'import/resolver': {
alias: {
map: [
['@', './src'],
['@components', './src/components'],
['@utils', './src/utils'],
],
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
},
},
},
};Or use eslint-import-resolver-vite for Vite projects:
npm install --save-dev vite-plugin-eslint eslint-import-resolver-vite// .eslintrc.js
module.exports = {
settings: {
'import/resolver': {
vite: {
configPath: './vite.config.ts',
},
},
},
};Fix 4: Fix node_modules Resolution Failures
If ESLint cannot resolve a package you have installed:
Check the package actually exists:
ls node_modules/date-fns
ls node_modules/@types/date-fnsAdd node to the resolver list:
// .eslintrc.js
module.exports = {
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
moduleDirectory: ['node_modules', 'src'],
},
typescript: {
alwaysTryTypes: true,
},
},
},
};For packages with non-standard exports fields (ESM-only packages):
Some modern packages use the exports field in package.json instead of main. ESLint’s resolver may not support this:
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
extensionAlias: {
'.js': ['.ts', '.tsx', '.js', '.jsx'],
},
},
},
},Add to import/ignore for packages you cannot resolve:
rules: {
'import/no-unresolved': ['error', {
ignore: [
'^virtual:', // Vite virtual modules
'^@astro/', // Astro built-in modules
],
}],
},Fix 5: Fix for Specific Frameworks
Next.js — aliases and built-in modules:
npm install --save-dev eslint-import-resolver-typescript// .eslintrc.js
module.exports = {
extends: ['next/core-web-vitals'],
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
};Next.js’s @/ alias is defined in tsconfig.json by default when created with create-next-app — the resolver picks it up automatically.
Vite + React — handle Vite-specific virtual modules:
// .eslintrc.js
module.exports = {
settings: {
'import/resolver': {
typescript: { alwaysTryTypes: true },
},
},
rules: {
'import/no-unresolved': ['error', {
ignore: ['^virtual:'], // Vite virtual modules like 'virtual:pwa-register'
}],
},
};React Native — resolve .ios.js and .android.js extensions:
settings: {
'import/resolver': {
node: {
extensions: [
'.ios.js', '.android.js',
'.ios.ts', '.android.ts',
'.js', '.jsx', '.ts', '.tsx',
],
},
},
},Fix 6: Disable the Rule for Specific Lines or Files
If you have a module that legitimately cannot be resolved by ESLint (e.g., environment-specific virtual module):
// Disable for a single line
import styles from './app.module.css'; // eslint-disable-line import/no-unresolved
// Disable for entire file
/* eslint-disable import/no-unresolved */
import { plugin } from 'some-virtual-module';
/* eslint-enable import/no-unresolved */Disable in .eslintrc for a path pattern:
module.exports = {
overrides: [
{
files: ['*.stories.tsx', '*.test.tsx'],
rules: {
'import/no-unresolved': 'off', // Test files can import test utilities freely
},
},
],
};Fix 7: Turn Off import/no-unresolved When Using TypeScript
If you use TypeScript with @typescript-eslint, TypeScript already catches invalid imports. The import/no-unresolved rule becomes redundant and often causes false positives:
// .eslintrc.js — valid approach for TypeScript projects
module.exports = {
extends: [
'@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
],
rules: {
// TypeScript handles module resolution — disable the duplicate ESLint check
'import/no-unresolved': 'off',
// Keep other import rules that TypeScript doesn't cover
'import/order': ['warn', { alphabetize: { order: 'asc' } }],
'import/no-duplicates': 'error',
'import/no-cycle': 'warn',
},
};When to disable vs. configure: If your project uses TypeScript strictly (
strict: true, noany), disablingimport/no-unresolvedis reasonable — TypeScript’smoduleResolutioncatches bad imports at compile time. If you have loose TypeScript settings or JavaScript files, keep the rule and configure the resolver properly.
Still Not Working?
Restart the ESLint language server. ESLint servers in VS Code cache configuration. After changing .eslintrc.js:
- VS Code: Ctrl+Shift+P → “ESLint: Restart ESLint Server”
- Or close and reopen the file
Check ESLint version compatibility. eslint-plugin-import v2 works with ESLint v8. For ESLint v9 (flat config), use eslint-plugin-import v2.29+ or the fork eslint-plugin-import-x:
npx eslint --version
npm list eslint-plugin-importRun ESLint from the command line to see the actual error:
npx eslint src/App.tsx --rule '{"import/no-unresolved": "error"}' --debug 2>&1 | grep -i "resolve\|alias"Check for missing @types packages:
# If you get import/no-unresolved for a JS package
npm install --save-dev @types/package-nameFor related ESLint and TypeScript tooling issues, see Fix: ESLint Parsing Error Unexpected Token and Fix: TypeScript Cannot Find Module.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: TypeScript isolatedModules Errors (const enum, type-only imports)
How to fix TypeScript isolatedModules errors — why const enum fails with Babel and Vite, how to replace const enum, fix re-exported types, and configure isolatedModules correctly for your build tool.
Fix: TypeScript Decorators Not Working (experimentalDecorators)
How to fix TypeScript decorators not applying — experimentalDecorators not enabled, emitDecoratorMetadata missing, reflect-metadata not imported, and decorator ordering issues.
Fix: Next.js Middleware Not Running (middleware.ts Not Intercepting Requests)
How to fix Next.js middleware not executing — wrong file location, matcher config errors, middleware not intercepting API routes, and how to debug middleware execution in Next.js 13 and 14.
Fix: Vitest Setup Not Working (setupFiles, Mocks, and Global Config Issues)
How to fix Vitest configuration not taking effect — why setupFiles don't run, globals are undefined, mocks don't work, and how to configure Vitest correctly for React, Vue, and Node.js projects.