Fix: Jest Cannot Find Module Error
Quick Answer
How to fix the Jest 'Cannot find module' error by configuring moduleNameMapper, moduleDirectories, tsconfig paths, and resolving ESM, monorepo, and dependency issues.
The Error
You run your test suite and Jest throws one of these errors:
FAIL src/__tests__/app.test.js
● Test suite failed to run
Cannot find module '@/utils/helpers' from 'src/components/App.js'Or a variation referencing a specific package:
Cannot find module 'axios' from 'src/services/api.js'Or when importing non-JavaScript assets:
Cannot find module './logo.svg' from 'src/components/Header.js'The code works fine in the browser or with your bundler, but Jest refuses to resolve the module. This happens because Jest uses its own module resolution system, separate from Webpack, Vite, or the TypeScript compiler. If you have not explicitly told Jest how to find your modules, it will fail.
Why This Happens
Jest resolves modules using Node.js-style resolution by default. It does not know about:
- Path aliases like
@/or~/configured in your bundler ortsconfig.json - Custom module directories beyond
node_modules - Non-JavaScript files like
.css,.svg, or.pngthat bundlers handle via loaders - Package subpath exports defined in
package.jsonexportsfield - ESM-only packages when Jest runs in CommonJS mode
- Monorepo packages that live in sibling directories
Jest reads its configuration from jest.config.js, jest.config.ts, or the jest key in package.json. If none of these files tell Jest how to map your imports, resolution fails.
The most common root causes:
- Missing
moduleNameMapper— path aliases are not mapped for Jest - Missing
moduleDirectories— custom module directories are not registered - TypeScript path mappings not mirrored —
tsconfig.jsonpathsexist but Jest does not know about them - Dependency not installed — a package is missing from
node_modules - Mocking not configured — Jest tries to resolve a module that should be mocked
jest.config.jsmisconfiguration — wrongroots,rootDir, ortestEnvironment- Monorepo resolution issues — packages in other workspaces are not visible
- ESM vs CJS mismatch — Jest runs CommonJS by default but the module is ESM-only
Fix 1: Configure moduleNameMapper for Path Aliases
If your project uses path aliases like @/, ~/, or #/, you must tell Jest how to resolve them. This is the most common cause of “Cannot find module” in Jest.
Open your jest.config.js and add a moduleNameMapper entry:
// jest.config.js
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^~/(.*)$': '<rootDir>/src/$1',
},
};The <rootDir> token refers to the root of your project (where package.json lives). The (.*)$ capture group maps everything after the alias prefix to the actual path.
For multiple aliases, add one entry per alias:
moduleNameMapper: {
'^@components/(.*)$': '<rootDir>/src/components/$1',
'^@utils/(.*)$': '<rootDir>/src/utils/$1',
'^@hooks/(.*)$': '<rootDir>/src/hooks/$1',
'^@services/(.*)$': '<rootDir>/src/services/$1',
},If you are using Webpack, match your resolve.alias configuration:
// webpack.config.js
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
};
// jest.config.js — must mirror the alias
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};For Vite projects, mirror the resolve.alias from vite.config.ts:
// vite.config.ts
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
});
// jest.config.js
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};Common Mistake: The order of entries in moduleNameMapper matters. Jest evaluates them top to bottom and uses the first match. If you have a general '^@/(.*)$' rule and a more specific '^@components/(.*)$' rule, put the more specific one first. Otherwise the general rule catches everything.
For non-JavaScript files that bundlers handle (CSS, images, SVGs), map them to a mock file:
moduleNameMapper: {
'\\.(css|less|scss|sass)$': '<rootDir>/__mocks__/styleMock.js',
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
'^@/(.*)$': '<rootDir>/src/$1',
},Create the mock files:
// __mocks__/styleMock.js
module.exports = {};
// __mocks__/fileMock.js
module.exports = 'test-file-stub';Fix 2: Set moduleDirectories for Custom Module Paths
If your project resolves modules from directories other than node_modules — for example, importing directly from src/ without a relative path — you need moduleDirectories:
// jest.config.js
module.exports = {
moduleDirectories: ['node_modules', 'src'],
};This tells Jest to look in both node_modules and src when resolving bare imports. So an import like import helpers from 'utils/helpers' resolves to src/utils/helpers.js.
Warning: Adding src to moduleDirectories can cause conflicts if a folder name inside src/ matches an npm package name. For example, if you have src/path/ and also use the path Node.js built-in, Jest might resolve to the wrong one. Use moduleNameMapper with a prefix alias instead if this is a concern.
You can also use the modules option in combination with roots:
module.exports = {
roots: ['<rootDir>/src'],
moduleDirectories: ['node_modules', '<rootDir>/src'],
};Fix 3: Mirror TypeScript paths in Jest Config
If you use TypeScript path mappings in tsconfig.json, Jest does not read them automatically. You must either mirror them manually or use a tool like ts-jest with pathsToModuleNameMapper.
Your tsconfig.json might look like this:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@lib/*": ["src/lib/*"]
}
}
}Option A: Manual mapping — duplicate each path in jest.config.js:
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@components/(.*)$': '<rootDir>/src/components/$1',
'^@lib/(.*)$': '<rootDir>/src/lib/$1',
},
};Option B: Use pathsToModuleNameMapper from ts-jest — this reads your tsconfig.json paths automatically:
const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.json');
module.exports = {
preset: 'ts-jest',
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>/',
}),
};The prefix option prepends <rootDir>/ to every mapped path, which is required because TypeScript paths are relative to baseUrl but Jest needs paths relative to the project root.
If your tsconfig.json uses extends to inherit from another config, make sure you are reading the final merged config. The compilerOptions.paths from the base config may not appear in your local tsconfig.json. In that case, read from the file that actually defines the paths, or use a tool like tsconfig-paths to resolve the full config.
If you are working with TypeScript and hit module errors, also check your TypeScript Cannot Find Module configuration — the root cause often overlaps.
Fix 4: Install Missing Dependencies
Sometimes the error is straightforward — the package is not installed:
Cannot find module 'lodash' from 'src/utils/format.js'Check if the package exists in node_modules:
ls node_modules/lodashIf it is missing, install it:
npm install lodashIf the package is listed in package.json but not in node_modules, your install may be corrupted. Delete node_modules and reinstall:
rm -rf node_modules package-lock.json
npm installFor peer dependencies, a package might expect another package to be installed at the top level. Check the error output for hints about which peer dependency is needed. This is common with testing libraries like @testing-library/react which expects react and react-dom as peers.
If you are seeing npm ERR! messages during install, see Fix: npm ERR! code ELIFECYCLE for common solutions.
Pro Tip: Run npm ls <package-name> to check if a package is installed and which version is resolved. If the output shows UNMET DEPENDENCY or deduped with a wrong version, that is your problem. You can also run npm ls --all to see the full dependency tree, but on large projects this can be slow.
Fix 5: Mock Modules Jest Cannot Resolve
Some modules should not be resolved at all during testing. CSS modules, image imports, and third-party modules with native bindings are common examples. Instead of resolving them, mock them.
For CSS Modules, use identity-obj-proxy:
npm install --save-dev identity-obj-proxy// jest.config.js
module.exports = {
moduleNameMapper: {
'\\.(css|less|scss)$': 'identity-obj-proxy',
},
};This makes every CSS class name resolve to itself. So styles.container returns "container" in tests, which is usually what you want for snapshot testing.
For specific packages that cause resolution issues, create manual mocks. Place a file in __mocks__/ at the root of your project:
// __mocks__/problematic-package.js
module.exports = {
doSomething: jest.fn(),
default: jest.fn(),
};Jest automatically picks up files in __mocks__/ that match the module name. For scoped packages like @scope/package, create the directory structure __mocks__/@scope/package.js.
You can also mock modules inline in your test file:
jest.mock('some-native-module', () => ({
nativeMethod: jest.fn().mockReturnValue('mocked'),
}));
import { nativeMethod } from 'some-native-module';Warning: The jest.mock() call is hoisted to the top of the file by Jest’s transform, so it runs before any imports. You do not need to place it before your import statements, but it helps readability to do so.
Fix 6: Fix jest.config.js Setup Issues
A misconfigured jest.config.js can cause module resolution to fail in subtle ways. Check these settings:
rootDir — defaults to the directory containing the config file. If your config is in a subdirectory, set rootDir explicitly:
module.exports = {
rootDir: '../',
};roots — tells Jest where to look for test files and source files. If your source lives in src/:
module.exports = {
roots: ['<rootDir>/src'],
};If roots is set to only ['<rootDir>/src'] but your tests import from <rootDir>/lib, Jest will not find those modules.
testEnvironment — affects which globals are available. If you are testing Node.js code, use node. If you are testing browser code, use jsdom:
module.exports = {
testEnvironment: 'jsdom', // or 'node'
};The wrong environment can cause modules that depend on browser APIs (like window or document) to fail.
transform — tells Jest how to process files before running them. If you use TypeScript, Babel, or other transpilers, you must configure the transform:
module.exports = {
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.jsx?$': 'babel-jest',
},
};Without the right transform, Jest tries to run TypeScript or JSX files as plain JavaScript and fails. This often manifests as a “Cannot find module” error when the transform should have rewritten an import path.
transformIgnorePatterns — by default, Jest does not transform files inside node_modules. If a dependency ships untranspiled ESM code, you need to exclude it from the ignore pattern:
module.exports = {
transformIgnorePatterns: [
'/node_modules/(?!(esm-only-package|another-esm-package)/)',
],
};This negative lookahead tells Jest to transform esm-only-package and another-esm-package while still skipping everything else in node_modules. You will see this pattern a lot when dealing with modern packages — see the next fix for more on ESM issues.
If you are migrating from an older version of Jest, check that your config file format is compatible. Jest 27+ uses jest.config.js or jest.config.ts. Older .jest.config.js naming does not work. Similarly, if you use import syntax in your config and get a module error, rename the config to .mjs or switch to require.
Fix 7: Fix Monorepo Module Resolution
In monorepos using Yarn Workspaces, npm Workspaces, or pnpm, Jest often fails to find packages from sibling workspaces:
Cannot find module '@myorg/shared-utils' from 'packages/web/src/app.js'The fix depends on your monorepo tool.
For Yarn/npm Workspaces, packages are usually hoisted to the root node_modules. Set rootDir to the monorepo root:
// packages/web/jest.config.js
module.exports = {
rootDir: '../../',
roots: ['<rootDir>/packages/web/src'],
moduleNameMapper: {
'^@myorg/shared-utils$': '<rootDir>/packages/shared-utils/src/index.ts',
},
};For pnpm, dependencies are not hoisted by default. You need to either configure pnpm to hoist them or use moduleDirectories to point Jest to the right place:
module.exports = {
moduleDirectories: [
'node_modules',
'<rootDir>/../../node_modules', // root node_modules in monorepo
],
};Using Jest projects — for monorepos with multiple packages that each need their own Jest config, use the projects feature in a root jest.config.js:
// Root jest.config.js
module.exports = {
projects: [
'<rootDir>/packages/web',
'<rootDir>/packages/api',
'<rootDir>/packages/shared',
],
};Each package directory should have its own jest.config.js with the appropriate moduleNameMapper and roots.
If your monorepo workspace packages use exports field in their package.json, make sure Jest can resolve the subpath exports. Jest 28+ supports package exports, but earlier versions do not. If you are on an older version, use moduleNameMapper to manually point to the correct entry file.
Also check that your workspace packages are properly linked. Run npm ls @myorg/shared-utils from the consuming package directory to verify the symlink is in place.
Fix 8: Resolve ESM vs CommonJS Conflicts
Modern npm packages increasingly ship as ESM-only (no CommonJS build). Jest, by default, runs in CommonJS mode and cannot require() ESM modules. This produces errors like:
Cannot find module 'esm-only-package' from 'src/app.js'Or sometimes:
SyntaxError: Unexpected token 'export'There are several approaches to fix this.
Option A: Transform the ESM package with Babel or ts-jest. Update transformIgnorePatterns to include the ESM package:
module.exports = {
transformIgnorePatterns: [
'/node_modules/(?!(esm-only-package|another-one)/)',
],
transform: {
'^.+\\.jsx?$': 'babel-jest',
},
};Make sure your Babel config can handle ESM syntax. A minimal .babelrc:
{
"presets": [
["@babel/preset-env", { "targets": { "node": "current" } }]
]
}Option B: Use Jest’s experimental ESM support. Set the --experimental-vm-modules flag:
NODE_OPTIONS='--experimental-vm-modules' npx jestAnd update your jest.config.js:
// jest.config.mjs (note the .mjs extension)
export default {
testEnvironment: 'node',
transform: {},
};This approach is still experimental as of Jest 29 and may have edge cases. For production test suites, Option A is more reliable.
Option C: Mock the ESM-only package if you do not need its actual implementation in tests:
jest.mock('esm-only-package', () => ({
someFunction: jest.fn(),
}));For a deeper look at the ESM vs CommonJS issue and how it affects Node.js imports generally, see Fix: Cannot use import statement outside a module.
If the ESM package uses top-level await, none of the CommonJS-based approaches work. You must either use Jest’s ESM mode or find a CJS-compatible alternative package.
Common Mistake: When adding a package to transformIgnorePatterns, you need to use a negative lookahead regex. The pattern /node_modules/(?!package-name/) means “ignore everything in node_modules EXCEPT package-name.” If you write /node_modules/package-name/ without the negative lookahead, you are telling Jest to ignore ONLY that package, which is the opposite of what you want.
Still Not Working?
If none of the fixes above solved your issue, try these additional steps:
Clear Jest’s cache. Jest caches transformed files and module resolution results. A stale cache can cause phantom module errors:
npx jest --clearCacheThen run your tests again. This is especially important after changing jest.config.js, updating dependencies, or switching branches.
Check for circular dependencies. If module A imports module B, and module B imports module A, Jest can fail to resolve one of them. Use a tool like madge to detect circular imports:
npx madge --circular src/Verify your package.json exports field. If you maintain a library with a package.json exports map, Jest 28+ resolves using those exports. Older Jest versions ignore the exports field entirely and fall back to main. If your exports field is wrong or missing a subpath, Jest will not find the module. For other common module resolution failures in Node.js, see Fix: Node.js ERR_MODULE_NOT_FOUND.
Check moduleFileExtensions. By default, Jest looks for .js, .jsx, .ts, .tsx, .json, and .node files. If your modules use an unusual extension like .mjs or .cjs, add it:
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs', 'cjs'],
};Run Jest with --verbose and --no-cache to see detailed module resolution output:
npx jest --verbose --no-cacheCheck for typos in import paths. On case-insensitive file systems (macOS, Windows), import from './Utils' works locally but fails in CI (Linux) where the file is utils.js. This is a common source of “works on my machine” module errors. If your environment variables are not loading correctly in the test environment, that could also cause downstream module failures when modules depend on process.env values at import time.
Use jest --showConfig to see the fully resolved Jest configuration, including all module resolution settings:
npx jest --showConfigLook at the moduleNameMapper, moduleDirectories, roots, and modulePaths fields to verify they match what you expect. If you see unexpected values, another config file or a preset might be overriding your settings.
If your project also uses Webpack and you see a similar error during builds, check Fix: Module not found: Can’t resolve — the Webpack-side fix is different from the Jest-side fix, but the root cause (missing alias configuration) is often the same.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Firebase Permission Denied Error
How to fix the Firebase 'permission denied' or 'Missing or insufficient permissions' error in Firestore and Realtime Database. Covers security rules, authentication state, custom claims, Admin SDK, rule simulators, time-based rules, and document-level permissions.
Fix: React Warning: Failed prop type
How to fix the React 'Warning: Failed prop type' error. Covers wrong prop types, missing required props, children type issues, shape and oneOf PropTypes, migrating to TypeScript, default props, and third-party component mismatches.
Fix: SyntaxError: Cannot use import statement outside a module
How to fix 'SyntaxError: Cannot use import statement outside a module' in Node.js, TypeScript, Jest, and browsers by configuring ESM, package.json type, and transpiler settings.
Fix: Express Cannot GET /route (404 Not Found)
How to fix Express.js Cannot GET route 404 error caused by wrong route paths, missing middleware, route order issues, static files, and router mounting problems.