Skip to content

Fix: Webpack Module Not Found – Can't Resolve '<module>' in '<directory>'

FixDevs ·

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:

  1. 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.
  2. Is it a package name? If there is no path prefix, Webpack searches node_modules directories, walking up the directory tree.
  3. Does a resolve.alias match? If you configured aliases in your Webpack config, Webpack checks those mappings.
  4. Does the file extension resolve? Webpack tries appending each extension listed in resolve.extensions (by default .js, .json, and .wasm in Webpack 5).
  5. Is it an externals entry? If the module is listed in externals, 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 lodash

If 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 install

Check whether the package is actually present:

ls node_modules/lodash

If you are using Yarn or pnpm, use the equivalent commands:

# Yarn
yarn install

# pnpm
pnpm install

A 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 updated package.json. A quick npm install styled-components fixes it, but always check git diff on package.json to 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.jsx

Common mistakes:

  • Typo in the file or directory name. Heder instead of Header, uitls instead of utils.
  • Wrong nesting level. Using ./components/Button when the file is at ../components/Button relative 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.jsx to Header.jsx) may not register as a change in Git on macOS or Windows. Use git mv header.jsx Header.jsx to ensure Git tracks the rename, otherwise your CI build on Linux will still see the old filename and fail.

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 a Can't resolve error when the expected package is not in the symlinked directory’s node_modules.
  • Resolution escaping the project. Webpack may follow a symlink to a directory outside your project, where node_modules does not exist.
// 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
    ],
  },
};

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-externals
const 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-plugin
const 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.json

Look 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 999

This 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-plugin
const 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-plugin
const 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 webpack

If 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/webpack

Related:

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles