Module not found: Can't resolve — How to Fix It (Next.js, Vite, webpack)
"Module not found: Error: Can't resolve 'x'" means your bundler couldn't find a file or package you imported. The fixes for each cause: a package that isn't installed, wrong relative paths, case-sensitive file names that work on Mac but fail on Linux, path aliases like @/, and Node built-ins imported in browser code.
Your build stops with something like:
Module not found: Error: Can't resolve 'axios' in '/app/src/lib'
Module not found: Can't resolve './components/Header'
Failed to resolve import "@/utils/format" from "src/App.tsx"
All of these mean the same thing: an import points at something the bundler (webpack, Turbopack or Vite) can't find. The fix depends on what kind of thing it is.
(If you see Cannot find module from Node.js itself rather than a bundler, see Cannot Find Module.)
Cause 1: The package isn't installed
If the name has no ./ or @/ in front — 'axios', 'date-fns', '@supabase/supabase-js' — it's a package from npm.
npm install axios
Check it's listed in package.json. AI tools often write imports for packages they forgot to add. And if a package was installed only on your machine (npm install without saving), the server's fresh install won't have it. (npm and package.json Explained)
Before installing a package an AI suggested, check it actually exists and is the real one — hallucinated package names are a known attack vector. (AI Hallucinated Packages)
Cause 2: The relative path is wrong
./ means "this folder", ../ means "one folder up". If src/pages/Home.tsx imports './components/Header', the bundler looks for src/pages/components/Header — probably wrong. It should be '../components/Header'. (File Paths Explained)
Cause 3: Capital letters (works locally, fails in production)
This is the classic. macOS and Windows file systems usually ignore case; Linux doesn't. So this works on your laptop and fails on the server:
import Header from './components/header' // file is Header.tsx
Make the import match the file name exactly. If you renamed a file only by case, git may not have noticed — use git mv header.tsx Header.tsx. (Why App Works Locally but Not in Production)
Cause 4: A path alias isn't configured
Imports like '@/utils/format' rely on an alias that maps @/ to src/. It needs to be set up for both TypeScript and the bundler:
// tsconfig.json
{
"compilerOptions": {
"paths": { "@/*": ["./src/*"] }
}
}
Next.js reads tsconfig.json paths automatically. Vite needs the alias too, in vite.config.ts (or a plugin such as vite-tsconfig-paths):
import path from 'node:path'
import { defineConfig } from 'vite'
export default defineConfig({
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
})
Cause 5: Node built-ins in browser code
Can't resolve 'fs', 'path', 'crypto' or 'child_process' means browser code imported something that only exists in Node.js. Usually a server-only file got imported by a client component. Move that code to the server side (an API route, a server action or a server component). (Frontend vs Backend)
Cause 6: Wrong file extension or missing index
Imports usually omit extensions, and the bundler tries .js, .ts, .tsx and folder index files. If the file is .mjs, .cjs or something unusual, include the extension. (ESM vs CommonJS)
When nothing obvious is wrong
npm ci
npm ci reinstalls dependencies from your existing lockfile. Then clear build caches like .next or node_modules/.vite and try again. Keep package-lock.json: deleting it resolves new versions and can introduce unrelated changes or malicious releases. (package-lock.json Explained)
The summary
- No
./→ install the package. With./→ fix the path. - Match capital letters exactly — Linux is case-sensitive.
@/needs an alias intsconfig.json(and Vite config).fs/patherrors mean server code leaked into the browser.
EasySpawn builds your app on the same Linux server it runs on, so case-sensitivity and missing-package surprises show up while you work — and Claude Code can fix them before you deploy. See how it works or join the waitlist.
Related: npm run build Fails · node_modules Explained · What Is Vite? · Webpack vs Vite
Keep reading
Unexpected End of JSON Input: Causes and Fixes
"SyntaxError: Unexpected end of JSON input" means you tried to parse JSON from an empty or cut-off string — most often calling response.json() on an empty response like a 204 or an error. How to find the real response, handle empty bodies safely, and fix the server side.
ReferenceError: X Is Not Defined — What It Means and How to Fix It
"Uncaught ReferenceError: x is not defined" means JavaScript can't find a variable or function with that name in the current scope. The common causes — typos, missing imports, scope, script load order, $ and jQuery, require in the browser, and window or document in server code — with a fix for each.