Introduction: Understanding the “Module not found: Error: Can't resolve 'web-vitals'”
When you run a modern React, Next.js, or Vite project and see the message “Module not found: Error: Can't resolve 'web-vitals'”, the build process stops in its tracks. That's why this error is more than a simple typo; it signals that the JavaScript bundler (Webpack, Vite, or similar) cannot locate the web-vitals package that your code—or a third‑party library—expects to import. Because web-vitals is the official library for measuring Core Web Vitals (LCP, FID, CLS, etc.), the error can affect performance monitoring, SEO scores, and even the stability of your development workflow.
In this article we will:
- Explain why the error occurs and which environments are most prone to it.
- Walk through a step‑by‑step troubleshooting guide, from the simplest fixes to deeper configuration changes.
- Clarify the relationship between
web-vitals, React‑based templates, and popular frameworks (Next.js, CRA, Vite, Remix). - Provide a set of best‑practice recommendations to prevent the issue from resurfacing.
By the end, you’ll not only be able to resolve the current error but also understand the underlying mechanisms that cause “module not found” problems, empowering you to maintain a smoother development experience.
1. What Is web-vitals and Why Is It Important?
web-vitals is an npm package published by Google that offers a lightweight API for collecting the Core Web Vitals—the metrics that Google uses to evaluate page experience. The package exports functions such as getCLS, getFID, getLCP, getFCP, and getTTFB. These functions can be called in the browser to send data to analytics tools (Google Analytics, GA4, Segment, etc.) or to log results during development Easy to understand, harder to ignore. And it works..
Basically where a lot of people lose the thread.
Because Core Web Vitals directly impact SEO rankings and user experience, many starter kits, performance dashboards, and commercial UI libraries include web-vitals as a dependency. If the module cannot be resolved, any code that tries to import it will throw the dreaded module not found error, halting the compilation.
2. Common Scenarios That Trigger the Error
| Scenario | Typical Symptoms | Why It Happens |
|---|---|---|
| Fresh clone of a repo | npm run dev fails immediately. In practice, |
Vite’s optimizer does not automatically install missing peer deps. |
| Custom Webpack alias | Path resolution points to a non‑existent folder. Because of that, | |
| Monorepo with Yarn Workspaces | Only one workspace throws the error. | |
| Using Vite with React | Vite dev server shows red overlay with the error. | |
| Upgrading from CRA v4 to v5 | Build succeeds locally, CI fails with “Can't resolve 'web-vitals'”. In practice, | Dependency hoisting placed web-vitals at a level not reachable by the failing package. jsonlistsweb-vitals` as a peerDependency but it was never installed. |
Understanding the context helps you select the most efficient fix.
3. Step‑by‑Step Troubleshooting Guide
3.1 Verify the Package Is Declared in package.json
Open your project’s package.json and look for web-vitals under dependencies or devDependencies Which is the point..
{
"dependencies": {
"react": "^18.2.0",
"web-vitals": "^3.3.0"
}
}
If it’s missing, add it:
npm install web-vitals --save # or yarn add web-vitals
Tip: If you see
web-vitalsonly underpeerDependencies, you must install it explicitly because peer dependencies are not installed automatically.
3.2 Clean the Node Modules and Reinstall
Corrupted caches or partial installs can cause resolution failures Worth keeping that in mind..
rm -rf node_modules package-lock.json # or yarn.lock
npm cache clean --force
npm install
For Yarn users:
yarn install --force
3.3 Check the Import Path
Make sure you are importing the package correctly:
import { getCLS, getFID, getLCP } from 'web-vitals';
Common mistakes:
- Adding a file extension:
'web-vitals.js'→ invalid. - Using a relative path:
'./web-vitals'→ looks for a local file, not the npm package.
If you see a typo like 'web-vial' or 'webVitals', correct it.
3.4 Inspect the Build Tool’s Resolve Configuration
Webpack
Open webpack.In practice, config. js (or the config generated by CRA) It's one of those things that adds up..
module.exports = {
// ...
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
// Example problematic alias
// '@': path.resolve(__dirname, 'src')
}
}
};
- Ensure no alias points to a folder named
web-vitals. - If you have
modules: ['node_modules', 'src'], keep the default order; movingsrcbeforenode_modulescan hide external packages.
Vite
In vite.config.js:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
// avoid aliasing 'web-vitals' to a non‑existent path
}
}
});
If you added an alias like { 'web-vitals': '/src/lib/web-vitals' }, remove it or rename the alias.
3.5 Verify the Package’s Physical Location
After a successful install, the package should exist at:
node_modules/web-vitals/
├─ package.json
├─ dist/
│ └─ web-vitals.esm.js
└─ src/
└─ index.js
If the folder is missing, the install step failed. Check the console output for permission errors or network timeouts.
3.6 Resolve Monorepo Hoisting Issues
In a Yarn workspace, the package may be hoisted to the root node_modules. Ensure the workspace’s package.json lists web-vitals as a dependency, or add a nohoist rule:
{
"workspaces": {
"packages": ["apps/*", "libs/*"],
"nohoist": ["**/web-vitals"]
}
}
Then run:
yarn install
3.7 Re‑run the Development Server
After applying the fixes, restart the server:
npm run dev # or yarn dev, pnpm dev, etc.
If the error disappears, you have resolved the issue. If it persists, proceed to the next diagnostic step But it adds up..
3.8 Dive Deeper: Use the Resolver Debug Flag
Webpack provides a debugging flag:
webpack --config webpack.config.js --display-modules --verbose
Search the output for web-vitals to see which directories were examined. This can reveal unexpected node_modules locations or symlink problems No workaround needed..
Vite’s resolver can be inspected by enabling the --debug flag:
vite --debug
Look for lines like resolve: searching for web-vitals to pinpoint the failure point Most people skip this — try not to..
4. Why the Error Often Appears After Upgrading Frameworks
4.1 Create React App (CRA) Transition
- CRA v4 bundled
web-vitalsas a dev dependency and generated a defaultreportWebVitals.jsfile that imported it automatically. - CRA v5 removed the automatic inclusion, assuming developers will add the package themselves. If you upgraded without installing
web-vitals, the import remains but the package is missing → error.
Fix: Run npm install web-vitals after upgrading, or delete the unused import if you don’t need Core Web Vitals.
4.2 Next.js 13 and the New App Router
Next.js 13 introduced the app directory, which encourages server‑side rendering. If you kept a client component that still imports web-vitals, the server build (which runs in a Node environment) cannot resolve the browser‑only package.
Solution:
-
Wrap the import in a client‑only component:
'use client'; import { getCLS } from 'web-vitals'; // … -
Or move the performance‑tracking code to a
useEffecthook that only runs in the browser Not complicated — just consistent..
4.3 Vite’s Optimized Dependencies
Vite pre‑bundles dependencies using esbuild. If web-vitals is listed under peerDependencies only, Vite may skip it during optimization, leading to a runtime resolution error Most people skip this — try not to..
Fix: Add it to optimizeDeps:
export default defineConfig({
optimizeDeps: {
include: ['web-vitals']
}
});
5. Frequently Asked Questions (FAQ)
Q1: Do I really need web-vitals in production?
A: No. The package is only required if you actively collect Core Web Vitals. You can safely remove the import and the associated reportWebVitals file for a leaner bundle And it works..
Q2: Can I replace web-vitals with a custom implementation?
A: Yes, but you would need to replicate the measurement logic defined by the Web Vitals specification, which is non‑trivial. Using the official library ensures compliance with Google’s measurement standards.
Q3: Why does the error appear only on CI pipelines and not locally?
A: CI environments often run a clean install without the local node_modules cache. If web-vitals is only listed as a peerDependency, the CI job will fail unless you explicitly add it to dependencies.
Q4: Is the error related to TypeScript?
A: Not directly. That said, if you have skipLibCheck disabled and the type definitions for web-vitals are missing, TypeScript may emit a compilation error that looks similar. Installing @types/web-vitals (if available) or adding a custom declaration file resolves the typing issue Practical, not theoretical..
Q5: What if I’m using pnpm?
A: pnpm creates a strict node_modules structure with symlinks. Ensure the package is present in the workspace’s node_modules/.pnpm folder. Running pnpm install --shamefully-hoist can temporarily work around hoisting problems And that's really what it comes down to..
6. Best Practices to Prevent Future “Module not found” Errors
-
Explicitly list all runtime dependencies – never rely solely on
peerDependenciesfor packages that your code imports directly It's one of those things that adds up.. -
Run
npm ci(oryarn install --frozen-lockfile) in CI – this guarantees the lockfile matches the installed modules, exposing missing packages early Took long enough.. -
Add a post‑install script that checks for critical packages:
"scripts": { "postinstall": "node -e \"try {require.resolve('web-vitals')} catch(e){process.exitCode=1}\"" } -
Keep framework upgrade guides handy – major releases of CRA, Next.js, or Vite often change default dependencies No workaround needed..
-
Use lint rules that flag unresolved imports – tools like ESLint’s
import/no-unresolvedcatch missing packages before the build starts. -
Document performance‑tracking code – place all
web-vitalsimports in a dedicated file (src/metrics/webVitals.js) and reference it from a single entry point. This centralization makes it easier to add or remove the dependency.
7. Conclusion
The “Module not found: Error: Can't resolve 'web-vitals'” message is a clear indicator that the bundler cannot locate the web-vitals package required for Core Web Vitals measurement. By confirming the package’s presence in package.json, reinstalling dependencies, verifying import syntax, and reviewing resolver configurations, you can eliminate the error in minutes.
On top of that, understanding why the issue surfaces—especially after framework upgrades or within monorepos—helps you adopt preventive habits such as explicit dependency declarations, proper workspace hoisting, and CI‑friendly install scripts.
Implement the troubleshooting steps and best‑practice checklist provided, and you’ll not only fix the current breakage but also safeguard future projects against similar module resolution pitfalls. Your applications will stay performant, your SEO metrics will be measurable, and your development workflow will remain smooth and frustration‑free.