LEARN · DEBUGGING GUIDE

React Native Metro Bundler Error: Complete Debugging Guide

Metro bundler errors in React Native are often caused by stale cache, missing dependencies, or incorrect module resolution. Here's how to diagnose and fix them fast.

IntermediateMobile6 min read

What this usually means

The Metro bundler is the JavaScript bundler for React Native. It transforms and bundles your code and dependencies into a single file. Errors typically arise from cache inconsistency (resolved module paths change but cache doesn't refresh), missing native modules (e.g., after npm install), version mismatches between React Native and its dependencies (especially with Expo or third-party libraries), or Babel configuration issues like missing presets or plugins.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Run `npx react-native start --reset-cache` to clear the Metro cache and restart the bundler.
  • 2Check the full error message and stack trace in the terminal or Metro web interface. Look for the module name that failed.
  • 3Verify the module is installed: `ls node_modules/<module-name>`. If missing, run `npm install` or `yarn install`.
  • 4Check for duplicate React issues: `npm ls react` to see if multiple versions exist. Use resolutions in package.json if needed.
  • 5Examine Babel config (babel.config.js) for missing presets like `module:metro-react-native-babel-preset`. Verify plugins are compatible.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchTerminal output from `npx react-native start` or `npx react-native run-android/ios`
  • searchMetro bundler web interface at http://localhost:8081 (if enabled)
  • searchpackage.json: check dependencies versions, especially react-native and metro-react-native-babel-preset
  • searchbabel.config.js: ensure correct presets and plugins
  • searchmetro.config.js: verify custom resolver settings (if any)
  • searchnode_modules/.cache/metro/ - clear this directory manually if needed
  • searchAppDelegate.m or MainApplication.java for native module imports
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningStale Metro cache after dependency changes (add/remove packages)
  • warningMissing or incompatible metro-react-native-babel-preset version
  • warningMultiple React instances (e.g., linked local packages with their own react)
  • warningIncorrect module name casing (macOS vs Linux case-sensitivity in imports)
  • warningBabel plugin version mismatch (e.g., @babel/preset-env with React Native)
  • warningSymlinked packages (yarn link or npm link) causing module resolution failures
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildClear all caches: `npx react-native start --reset-cache` then `cd ios && pod deintegrate && pod install` (iOS)
  • buildReinstall dependencies: delete node_modules and run `npm install` or `yarn install`, then clear cache
  • buildFix Babel config: ensure `module.exports = { presets: ['module:metro-react-native-babel-preset'] }` in babel.config.js
  • buildResolve multiple React versions: add `"resolutions": { "react": "<version>" }` to package.json (yarn) or use `npm dedupe`
  • buildUse correct module paths: check import statements for typos and case differences
  • buildDisable symlinks in Metro: add `const { getDefaultConfig } = require('metro-config'); module.exports = (async () => { const defaultConfig = await getDefaultConfig(); return { ...defaultConfig, resolver: { ...defaultConfig.resolver, disableHierarchicalLookup: true } }; })();`
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedRun `npx react-native start --reset-cache` and reload app; bundle should compile without errors
  • verifiedVerify module resolution: check that the previously failing module is now correctly bundled
  • verifiedTest on both iOS and Android simulators/emulators to ensure cross-platform consistency
  • verifiedRun `npm ls <module-name>` to confirm only one version exists
  • verifiedCheck Metro web interface for successful bundle build with no errors
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningDo not delete node_modules entirely without also clearing Metro cache – partial fix
  • warningDo not ignore version mismatches – always check peer dependencies
  • warningDo not manually edit metro.config.js without understanding default config
  • warningDo not use absolute paths in imports unless configured correctly
  • warningDo not forget to run `pod install` for iOS after dependency changes
( 07 )War story

Metro bundler crashes after adding react-native-maps

Mobile EngineerReact Native 0.64.2, react-native-maps 0.27.1, macOS Big Sur, iOS 14.5

Timeline

  1. 09:00Run `npm install react-native-maps` following docs
  2. 09:02Run `npx react-native run-ios`; Metro starts but fails with error: 'Unable to resolve module react-native-maps'.
  3. 09:05Check node_modules/react-native-maps exists. Try `npx react-native start --reset-cache` – no change.
  4. 09:10Search error: Stack Overflow suggests checking Babel preset. My babel.config.js uses @babel/preset-env.
  5. 09:15Changed babel.config.js to use `module:metro-react-native-babel-preset`. Still fails.
  6. 09:20Check react-native-maps docs: requires pod install for iOS. Run `cd ios && pod install`; pod install fails with 'React pod not found'.
  7. 09:25Realize react-native-maps expects React to be installed as a pod. My project uses auto-linking? Check Podfile: missing `use_frameworks!` and React pod. Add `pod 'react-native-maps', :path => '../node_modules/react-native-maps'`
  8. 09:30Run `pod install` again; success. Run `npx react-native run-ios` – app builds and loads map.

I was adding react-native-maps to an existing React Native 0.64 app. I followed the npm install step and then immediately tried to run the app on iOS simulator. Metro bundler failed with a module resolution error for react-native-maps. I assumed it was a cache issue, so I cleared the cache multiple times, but the error persisted.

After wasting 20 minutes on cache clearing, I checked the Babel config. Earlier I had customized it for some experimental features, so I thought maybe the preset was wrong. I switched from @babel/preset-env to the recommended metro preset, but that didn't fix it. That's when I remembered that some native modules require native linking. For iOS, react-native-maps needs to be added to the Podfile.

I opened the Podfile and added the pod spec for react-native-maps. But pod install then failed because the React pod wasn't found. I had to ensure the React pod was correctly referenced from the node_modules. After fixing the Podfile, pod install succeeded, and the app ran correctly. The lesson: always check native linking steps for third-party modules, even in auto-linking projects.

Root cause

Missing pod 'react-native-maps' in Podfile caused Metro bundler to fail resolving the module because the native module wasn't linked.

The fix

Added `pod 'react-native-maps', :path => '../node_modules/react-native-maps'` to Podfile and ran `pod install`.

The lesson

Metro bundler errors after adding a module often indicate missing native linking, not JavaScript issues. Always check the module's installation guide for native steps.

( 08 )Metro Cache Mechanism and Reset Strategies

Metro maintains a file system cache at `node_modules/.cache/metro/` to speed up subsequent builds. This cache includes transformed module code, dependency graphs, and serialized bundles. When you change dependencies (add/remove packages), the cache may contain stale module paths or references, leading to 'Unable to resolve module' errors even though the file exists.

The most reliable reset is `npx react-native start --reset-cache`. This deletes the entire metro cache directory and forces a fresh rebuild. You can also manually delete `node_modules/.cache/metro/` but the CLI flag is safer. For persistent issues, combine with `watchman watch-del-all` and `rm -rf $TMPDIR/metro-*` to clear Watchman and temp files.

( 09 )Babel Preset Conflicts in React Native

React Native requires the `metro-react-native-babel-preset` preset. Using generic presets like `@babel/preset-env` or `@babel/preset-react` can cause transform errors (e.g., 'Support for the experimental syntax 'classProperties' isn't currently enabled'). The metro preset includes all necessary transforms for React Native, including Flow and TypeScript support.

If you have custom Babel plugins (e.g., for styled-components or decorators), ensure they are compatible with the metro preset. A common mistake is adding `@babel/plugin-proposal-class-properties` which conflicts with the metro preset's own class properties transform. Always check plugin order: the metro preset should be first in the presets array.

( 10 )Module Resolution and Case Sensitivity

Metro resolves modules based on the file system. On macOS (case-insensitive by default), `import Foo from './components/Foo'` works even if the file is `./Components/Foo`. But on Linux (case-sensitive), this fails. Many Metro errors on CI or production builds stem from case mismatches that work locally.

To diagnose, check the exact path in the error message and compare with the actual file path. Use `ls` to verify case. Fix by ensuring all imports match the actual file casing. Also, Metro's `resolver.extraNodeModules` can cause confusion if misconfigured. Avoid adding custom resolver paths unless necessary.

( 11 )Multiple React Instances and Hoisting Issues

If you have multiple React versions in node_modules (e.g., from linked packages or npm hoisting), Metro may pick the wrong one, causing 'Invalid hook call' or 'Unable to resolve module react' errors. This is common in monorepos or when using `npm link`.

Check with `npm ls react` or `yarn why react`. Fix by adding a `resolutions` field in package.json (for yarn) or using `npm dedupe`. Alternatively, configure Metro's `resolver.extraNodeModules` to force a single React instance: `const path = require('path'); extraNodeModules: { react: path.resolve(__dirname, 'node_modules/react') }`. This ensures all imports resolve to the same React library.

Frequently asked questions

Why does Metro fail with 'Module not found' even though the file exists?

This usually means Metro's dependency graph is stale. Run `npx react-native start --reset-cache` to rebuild the graph from scratch. If that fails, check for import case mismatches, symlink issues, or missing native linking (for native modules).

How do I fix TransformError related to class properties?

TransformError about class properties indicates your Babel config lacks the appropriate plugin. Use the `metro-react-native-babel-preset` which includes it. If you already have the preset, check for duplicate or conflicting plugins like `@babel/plugin-proposal-class-properties`. Remove it and rely solely on the preset.

What should I do when Metro error only occurs on iOS or Android but not both?

This often points to native linking differences. For iOS, ensure `pod install` was run after adding native modules. For Android, check `android/app/build.gradle` for missing `implementation` lines or `MainApplication.java` for package imports. Also verify that the module supports both platforms.

Can I use Expo's Metro config in a bare React Native project?

Expo's default Metro config includes additional resolvers and transformers. You can copy parts, but it may cause issues. Stick with `@react-native/metro-config` or the standard `metro.config.js` from the React Native template. Only customize if you understand the implications.

How do I prevent Metro cache issues in CI/CD pipelines?

Set the environment variable `METRO_CACHE_DIR` to a temporary directory that gets cleaned each build, e.g., `METRO_CACHE_DIR=$(mktemp -d)`. Also, invalidate the cache when package.json or yarn.lock changes by using a cache key based on their checksums.