Fix: Webpack Module Not Found – Can't Resolve '<module>' in '<directory>'
Quick Answer
How to fix the Webpack error 'Module not found: Error: Can't resolve' caused by missing packages, wrong paths, aliases, or extension resolution issues.
The Error
You run your Webpack build or start a dev server and hit one of these:
Missing an npm package:
Module not found: Error: Can't resolve 'lodash'
in '/home/user/my-app/src'Wrong relative import path:
Module not found: Error: Can't resolve './components/Heder'
in '/home/user/my-app/src/pages'Missing file extension:
Module not found: Error: Can't resolve './utils/format'
in '/home/user/my-app/src'Node.js core module not available (Webpack 5):
Module not found: Error: Can't resolve 'crypto'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.Alias not resolving:
Module not found: Error: Can't resolve '@components/Button'
in '/home/user/my-app/src/pages'All of these share the same root message: Module not found: Error: Can't resolve. Webpack tried to locate a module based on the string you passed to import or require() and could not find a matching file or package anywhere in its resolution chain.
Why This Happens
Webpack resolves modules using its own resolution algorithm, which is similar to Node.js resolution but with additional features like aliases, extension fallbacks, and module directories. When you write import something from './path' or import pkg from 'package-name', Webpack walks through a series of steps:
- Is it a relative or absolute path? If the import starts with
./,../, or/, Webpack looks for a file at that path relative to the importing file. - Is it a package name? If there is no path prefix, Webpack searches
node_modulesdirectories, walking up the directory tree. - Does a
resolve.aliasmatch? If you configured aliases in your Webpack config, Webpack checks those mappings. - Does the file extension resolve? Webpack tries appending each extension listed in
resolve.extensions(by default.js,.json, and.wasmin Webpack 5). - Is it an
externalsentry? If the module is listed inexternals, Webpack skips bundling it and expects it to be available at runtime.
If every step fails, Webpack throws Module not found: Error: Can't resolve. The causes range from a missing npm install to a subtle alias misconfiguration or a case-sensitivity mismatch between operating systems.
Fix 1: Install the Missing npm Package
The most common cause is that the package simply is not installed. If the error names a package (not a relative path), install it:
npm install lodashIf you already have it in package.json, the install may have failed or node_modules may be out of sync:
rm -rf node_modules package-lock.json
npm installCheck whether the package is actually present:
ls node_modules/lodashIf you are using Yarn or pnpm, use the equivalent commands:
# Yarn
yarn install
# pnpm
pnpm installA common mistake is installing a package globally and expecting Webpack to find it. Webpack only looks in the local node_modules by default. Always install packages locally for projects that bundle with Webpack. If the install itself fails with dependency conflicts, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree for resolving version conflicts.
Real-world scenario: You clone a teammate’s branch, run
npm start, and immediately get “Can’t resolve ‘styled-components’”. They added the package to their code but forgot to commit the updatedpackage.json. A quicknpm install styled-componentsfixes it, but always checkgit diffonpackage.jsonto confirm the dependency was actually added.
Fix 2: Correct the Relative Import Path
If the error references a relative path like ./components/Heder, you have a typo or the file structure does not match your import.
Verify the file exists at the expected location:
ls src/components/Header.jsxCommon mistakes:
- Typo in the file or directory name.
Hederinstead ofHeader,uitlsinstead ofutils. - Wrong nesting level. Using
./components/Buttonwhen the file is at../components/Buttonrelative to the importing file. - Importing a directory without an index file.
import App from './App'expects either./App.js(or another resolved extension) or./App/index.js.
Double-check the exact path by looking at your directory structure. The error message tells you the directory Webpack was searching from (the in '/path/to/dir' part), so you can reconstruct the full path it tried.
Fix 3: Add Missing Extensions to resolve.extensions
Webpack only auto-resolves certain file extensions. By default in Webpack 5, resolve.extensions is set to ['.js', '.json', '.wasm']. If you import a .ts, .tsx, .jsx, or .mjs file without specifying the extension, Webpack will not find it.
Add the extensions your project uses:
// webpack.config.js
module.exports = {
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
},
};Now import Button from './Button' will match ./Button.tsx or ./Button.jsx without an explicit extension.
Order matters. Webpack tries extensions from left to right and uses the first match. If you have both Button.js and Button.tsx in the same directory, the one matching the earlier extension wins. Put your most common extension first.
A related problem occurs if you import a CSS or SCSS file and forget the extension. CSS files should always be imported with their extension (import './styles.css'), since adding .css to resolve.extensions can cause unexpected resolution conflicts. If Webpack chokes on the CSS file’s contents after resolving it, the issue is a missing loader instead — see Fix: Module parse failed: Unexpected token in Webpack for that.
Fix 4: Fix resolve.alias Misconfiguration
Webpack’s resolve.alias lets you create shorthand paths like @components or @utils. A broken alias is a frequent source of “Can’t resolve” errors.
The alias does not match the import
// webpack.config.js
const path = require('path');
module.exports = {
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components'),
},
},
};With this configuration, import Button from '@components/Button' works. But import Button from '@/components/Button' does not — the alias is @components, not @/components. The prefix must match exactly.
The alias target path is wrong
The path you pass to path.resolve must point to an existing directory or file. A common error is using a relative path instead of an absolute one:
// Wrong -- relative path, will break depending on cwd
alias: {
'@src': './src',
}
// Correct -- absolute path
alias: {
'@src': path.resolve(__dirname, 'src'),
}Alias and TypeScript paths out of sync
If you use TypeScript, your tsconfig.json paths must mirror your Webpack aliases. Otherwise TypeScript may accept the import (no red squiggles in your editor) but Webpack cannot resolve it at build time:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@utils/*": ["utils/*"]
}
}
}// webpack.config.js
const path = require('path');
module.exports = {
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
},
};Both sides must agree on the alias prefix and the target directory. If your TypeScript project shows module resolution errors at compile time as well, see Fix: Cannot find module or its corresponding type declarations in TypeScript for TypeScript-specific solutions.
Fix 5: Handle Case Sensitivity (Linux vs. macOS/Windows)
File systems on macOS and Windows are case-insensitive by default. import Header from './header' will find Header.jsx without issue on those platforms. On Linux, file systems are case-sensitive, so the same import fails because header and Header are different names.
This is a common cause of builds that pass locally but fail in CI/CD (which typically runs Linux). The fix is straightforward: match the exact casing of the file name in every import.
To catch case-sensitivity issues before they reach CI, use the case-sensitive-paths-webpack-plugin:
npm install --save-dev case-sensitive-paths-webpack-plugin// webpack.config.js
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
module.exports = {
plugins: [
new CaseSensitivePathsPlugin(),
],
};This plugin forces Webpack to error on case mismatches even on case-insensitive file systems, so you catch the problem during local development instead of discovering it in production.
Common Mistake: Renaming a file by only changing its casing (e.g.,
header.jsxtoHeader.jsx) may not register as a change in Git on macOS or Windows. Usegit mv header.jsx Header.jsxto ensure Git tracks the rename, otherwise your CI build on Linux will still see the old filename and fail.
Fix 6: Resolve Symlink Issues
Webpack follows symlinks by default (via resolve.symlinks, which defaults to true). This is usually fine, but it can cause problems in monorepos or when using npm link:
- Duplicate packages. If a symlinked package has its own
node_modules, Webpack may resolve a dependency from the symlinked location instead of the project root, leading to two copies of the same package or aCan't resolveerror when the expected package is not in the symlinked directory’snode_modules. - Resolution escaping the project. Webpack may follow a symlink to a directory outside your project, where
node_modulesdoes not exist.
Disable symlink resolution
// webpack.config.js
module.exports = {
resolve: {
symlinks: false,
},
};This tells Webpack to use the symlink path as-is instead of resolving to the real path. This keeps module resolution within the expected directory structure.
Add additional modules directories
If you are in a monorepo and packages cannot find shared dependencies, tell Webpack where to look:
// webpack.config.js
const path = require('path');
module.exports = {
resolve: {
modules: [
'node_modules',
path.resolve(__dirname, '../../node_modules'), // monorepo root
],
},
};Using npm link
When you npm link a local package for development, the linked package is a symlink in node_modules pointing to your local clone. If that linked package imports a dependency that is only installed in the main project’s node_modules, Webpack may fail to find it because it is resolving from the symlink target’s directory.
The fix is to add the main project’s node_modules to resolve.modules, or disable symlink resolution as shown above.
Fix 7: Fix externals Misconfiguration
Webpack’s externals option tells the bundler to skip certain modules and expect them to be available at runtime (e.g., loaded from a CDN or provided by the host environment). If you accidentally mark a module as external that is not actually available at runtime, you will get a runtime error. But if the externals configuration uses a regex or function that inadvertently matches imports you did not intend to exclude, Webpack may report “Can’t resolve” for those modules during the build.
Check your externals configuration
// webpack.config.js
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
};This is fine — it only excludes react and react-dom. But a regex-based external can be overly broad:
// Dangerous -- excludes everything in node_modules
externals: [/node_modules/],This tells Webpack not to bundle any module whose path matches node_modules, which effectively skips all your dependencies. In a server-side build (for Node.js), this is sometimes intentional — but it requires that all those packages are available at runtime via Node.js resolution. In a browser build, this breaks everything.
If you are building for Node.js and want to externalize dependencies, use webpack-node-externals for proper handling:
npm install --save-dev webpack-node-externalsconst nodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node',
externals: [nodeExternals()],
};webpack-node-externals only externalizes packages listed in package.json, which is safer than a blanket regex.
Fix 8: Add Fallback Polyfills for Node.js Core Modules (Webpack 5)
Webpack 4 automatically included polyfills for Node.js core modules like crypto, stream, buffer, path, and os. Webpack 5 removed this behavior. If your code (or a dependency) imports a Node.js core module, Webpack 5 throws:
Module not found: Error: Can't resolve 'crypto'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.Option A: Provide polyfills via resolve.fallback
If you need the module’s functionality in the browser, install a polyfill and add a fallback:
npm install --save-dev crypto-browserify stream-browserify buffer process// webpack.config.js
const webpack = require('webpack');
module.exports = {
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
buffer: require.resolve('buffer/'),
process: require.resolve('process/browser'),
path: require.resolve('path-browserify'),
os: require.resolve('os-browserify/browser'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
zlib: require.resolve('browserify-zlib'),
url: require.resolve('url/'),
assert: require.resolve('assert/'),
util: require.resolve('util/'),
},
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser',
}),
],
};You only need to add fallbacks for the modules that your code actually uses. The error message will tell you which module is missing.
Option B: Set the fallback to false
If the code path using the Node.js module is never actually reached in the browser (e.g., it is behind a server-only conditional), you can tell Webpack to substitute an empty module:
resolve: {
fallback: {
fs: false,
net: false,
tls: false,
dns: false,
child_process: false,
},
}This silences the error and provides an empty object at runtime. The code will break if it actually tries to use these modules — but if the code path is unreachable in the browser, that is fine.
Option C: Use node-polyfill-webpack-plugin
If you have many polyfills to configure, the node-polyfill-webpack-plugin package adds all of them at once:
npm install --save-dev node-polyfill-webpack-pluginconst NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
module.exports = {
plugins: [
new NodePolyfillPlugin(),
],
};This is the fastest way to migrate a Webpack 4 project to Webpack 5 without rewriting every Node.js core module import. However, it increases your bundle size. Once things are working, audit which polyfills are actually needed and switch to explicit resolve.fallback entries. For Node.js runtime module errors outside of Webpack, see Fix: Error Cannot find module in Node.js.
Fix 9: Verify the Package’s exports and main Fields
Some packages use the exports field in their package.json to restrict which files can be imported. If a package exposes only specific entry points, importing a subpath that is not listed in exports will fail:
Module not found: Error: Can't resolve 'some-package/utils'Check the package’s package.json:
cat node_modules/some-package/package.jsonLook for an exports field. If /utils is not listed, the import is intentionally blocked by the package author. You may need to import from the main entry point and access the utility from there, or check if a different subpath is available.
You can configure Webpack to use a different condition for the exports field via resolve.conditionNames:
// webpack.config.js
module.exports = {
resolve: {
conditionNames: ['import', 'module', 'browser', 'default'],
},
};Some packages export different entry points for different conditions (import for ESM, require for CJS, browser for browser builds). Setting the right condition names ensures Webpack picks up the correct entry point.
Still Not Working?
Enable Webpack’s detailed resolution logging
Webpack can log every step of its module resolution process. This is extremely useful for understanding exactly why a module is not found:
// webpack.config.js
module.exports = {
stats: {
logging: 'verbose',
loggingDebug: [/webpack\.cache/, /resolve/],
},
};Or from the command line:
npx webpack --stats-reasons --stats-modules-space 999This will print out every path Webpack tried, which extensions it appended, and which aliases it checked. The output is verbose but tells you exactly where resolution diverged from your expectations.
Check for circular dependencies
Circular imports do not usually cause “Can’t resolve” directly, but they can create confusing partial-load scenarios where an exported value is undefined. If you are getting “Can’t resolve” on a module that clearly exists, circular dependencies may be causing the module to appear empty or uninitialized at the time it is imported. Use circular-dependency-plugin to detect them:
npm install --save-dev circular-dependency-pluginconst CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = {
plugins: [
new CircularDependencyPlugin({
exclude: /node_modules/,
failOnError: true,
}),
],
};Verify your tsconfig.json paths are wired to Webpack
If you use TypeScript path aliases and the build fails only in Webpack (not in tsc), make sure you have tsconfig-paths-webpack-plugin installed and configured:
npm install --save-dev tsconfig-paths-webpack-pluginconst TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
resolve: {
plugins: [new TsconfigPathsPlugin()],
},
};This plugin reads your tsconfig.json paths and registers them as Webpack aliases automatically, so you do not have to duplicate the configuration.
Check if ESLint or another tool is interfering
If your build pipeline runs ESLint before Webpack, an ESLint import resolution error can be mistaken for a Webpack error. Check your full build output carefully. If the error comes from ESLint’s import plugin rather than Webpack, see Fix: ESLint Parsing error: Unexpected token for ESLint-specific fixes.
Rebuild after config changes
Webpack caches resolved modules aggressively. If you changed your resolve configuration but the error persists, clear the cache:
rm -rf node_modules/.cache
npx webpackIf you are using cache: { type: 'filesystem' } in your Webpack config, the cache is stored on disk and survives restarts. Delete it to force a full rebuild:
rm -rf node_modules/.cache/webpackRelated:
- Fix: Module parse failed: Unexpected token in Webpack — for errors where the module is found but cannot be parsed.
- Fix: Error Cannot find module in Node.js — for Node.js runtime module resolution errors outside of Webpack.
- Fix: Cannot find module or its corresponding type declarations in TypeScript — for TypeScript-specific import resolution failures.
- Fix: npm ERR! ERESOLVE unable to resolve dependency tree — for npm dependency conflicts during installation.
- Fix: ESLint Parsing error: Unexpected token — for ESLint configuration issues that produce misleading import errors.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Error: error:0308010C:digital envelope routines::unsupported
How to fix the Node.js digital envelope routines unsupported error caused by OpenSSL 3.0 changes in Node.js 17+, with solutions for webpack, Vite, React, and Angular.
Fix: Yarn Integrity Check Failed – Expected and Got Different Results
How to fix the Yarn error 'integrity check failed' or 'Lockfile does not satisfy expected package' caused by corrupted cache, outdated lockfile, or version mismatches.
Fix: CORS Error with Credentials – Access-Control-Allow-Credentials and Wildcard Origin
How to fix CORS errors when using cookies or Authorization headers, including 'Access-Control-Allow-Credentials' and wildcard origin conflicts.
Fix: Module parse failed: Unexpected token (Webpack / Vite / esbuild)
How to fix 'Module parse failed: Unexpected token' in Webpack, Vite, and esbuild by configuring the correct loaders and transforms for JSX, TypeScript, CSS, JSON, and other file types.