You run your tests and Jest throws:
Cannot find module '@/components/Button' from 'src/App.test.tsx'
Your app runs fine in the browser, but Jest can’t resolve the same imports.
What causes this
Jest uses its own module resolution system, completely separate from your bundler (Webpack, Vite, esbuild, etc.). Path aliases like @/, ~/, or #/ are configured in your bundler or tsconfig.json, but Jest doesn’t read those configs by default.
So when your code imports @/components/Button, Vite knows that @ maps to src/. Jest doesn’t — it tries to find a literal npm package called @/components/Button and fails.
You’ll also see this error when:
- Importing CSS, SCSS, images, or other non-JS files that Jest can’t parse
- A dependency isn’t installed or is missing from
node_modules - You’re using TypeScript path mappings without telling Jest about them
Fix 1: Add moduleNameMapper for path aliases
Tell Jest how to resolve your aliases in jest.config.js:
// jest.config.js
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
If you have multiple aliases:
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@components/(.*)$': '<rootDir>/src/components/$1',
'^@utils/(.*)$': '<rootDir>/src/utils/$1',
},
The keys are regex patterns, and $1 captures the rest of the path. <rootDir> is the directory containing your Jest config.
Fix 2: Handle CSS and asset imports
Jest can’t parse CSS or image files. You need to mock them:
// jest.config.js
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|svg|webp)$': '<rootDir>/__mocks__/fileMock.js',
},
Install the CSS mock:
npm install --save-dev identity-obj-proxy
Create the file mock:
// __mocks__/fileMock.js
module.exports = 'test-file-stub';
identity-obj-proxy returns the class name as-is, so styles.button returns "button" in tests. The file mock returns a string placeholder for images.
Fix 3: Sync with tsconfig paths automatically
If you’re using TypeScript, you can use ts-jest with pathsToModuleNameMapper to auto-generate the mappings from your tsconfig.json:
// jest.config.js
const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.json');
module.exports = {
preset: 'ts-jest',
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>/',
}),
};
This reads the paths from your tsconfig.json and converts them to Jest’s moduleNameMapper format. One source of truth — no manual syncing.
Fix 4: Check that the module actually exists
Sometimes the error is simpler than you think — the file doesn’t exist or the path is wrong:
# Verify the file exists
ls src/components/Button.tsx
# Check for case sensitivity issues (common on Linux CI)
find src/components -iname "button*"
macOS and Windows filesystems are case-insensitive by default, so Button and button resolve to the same file. Linux is case-sensitive — your tests might pass locally but fail in CI.
How to prevent it
- Set up
moduleNameMapperwhen you first add Jest to a project — don’t wait until it breaks - If using TypeScript, use
pathsToModuleNameMapperso aliases stay in sync automatically - Add CSS/asset mocks from the start — you’ll need them eventually
- Run tests in CI on Linux to catch case-sensitivity issues early
- Keep your Jest config close to your bundler config and update both when adding new aliases