Skip to content
Back to skills

Monorepo Metro

ASecurity

React Native and Expo in monorepos. Use for Invalid hook call or duplicate react-native in a workspace app, Metro not resolving workspace packages, EAS failing only in the monorepo, hoisting and lockfile strategy.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
ai-agentsgoreactnodedebugginggit

Security analysis

A100/100

Scanned September 22, 2026

npx -y skills add AnilBurcu/claude-code-react-native --skill monorepo-metro --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Monorepo Metro?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Monorepo Metro
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anilburcu-monorepo-metro/badge)](https://www.skillsdirectory.com/skills/anilburcu-monorepo-metro)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: monorepo-metro
description: React Native and Expo in monorepos. Use for Invalid hook call or duplicate react-native in a workspace app, Metro not resolving workspace packages, EAS failing only in the monorepo, hoisting and lockfile strategy.
user-invocable: false
---

# React Native in a monorepo

## First, delete the folklore

Modern expo/metro-config configures Metro for monorepos automatically. If the repo carries a hand-written `metro.config.js` full of `watchFolders`, `nodeModulesPaths`, `extraNodeModules` or `disableHierarchicalLookup` copied from an old blog post, that config is now the most likely cause of the problem, not the fix. Strip it down to the default Expo config, run `npx expo start --clear` once, and re-test before debugging anything else.

The problems that remain after that are real, and they are all below.

## Singletons, the actual monorepo disease

react, react-native and the native-module packages must resolve to exactly one copy for the app. Two copies produce the classic signatures:

- "Invalid hook call" with a perfectly valid component
- "Tried to register two views with the same name"
- A native module that works in one workspace app and crashes in another

Diagnose with the package manager, not by staring at code: `npm ls react-native` (or `yarn why react-native`, `pnpm why react-native`) from the repo root. More than one resolved version, or the same version in two physical locations, confirms it.

Fix at the root: pin the singletons with the root manifest's override mechanism (`overrides` for npm, `resolutions` for yarn, pnpm's overrides). Then reinstall from the root and re-run the check. If the lockfile seems to hold onto stale resolutions after the override, a full reinstall of node_modules is legitimate here; this is the deliberate-upgrade case, not build-error cache thrashing.

## Hoisting strategies decide your failure mode

- npm and yarn hoist: a package you forgot to declare still resolves because a sibling declared it. Everything works locally and then fails on EAS or on a colleague's machine with a different install order. The bug is the undeclared dependency, not the machine.
- pnpm and bun isolate: undeclared dependencies fail immediately and loudly. More friction on day one, dramatically fewer ghosts later.

Whichever the repo uses, the rule is the same: every workspace package declares what it imports. Hoisting merely changes when you find out.

## Native code and config plugins in shared packages

JS-only shared packages are free. The moment a shared package carries native code or a config plugin, remember that natives are built per app: the app's own package.json must carry the dependency (so autolinking and prebuild see it), not just the shared package's. Symptoms of getting this wrong: the module exists in JS, and the native side throws "module not found" only in release or only in one app.

## EAS builds from a monorepo

- The lockfile that matters is the root one; EAS installs from the repo root and builds the app in its workspace directory.
- "Works locally, fails on EAS" in a monorepo is almost always hoisting (an undeclared dependency that local hoisting hid) or a file referenced outside the workspace that is not committed.
- Keep one SDK/react-native version pair across apps in the workspace if at all possible. Two apps on two RN versions in one workspace is a supported-in-theory, painful-in-practice setup; if a version split is unavoidable, treat it as temporary and plan the convergence.

## When Metro acts haunted

Symptoms like "module disappeared after I moved a file" or imports resolving to stale copies: one `npx expo start --clear` after dependency-graph surgery is legitimate. Running it before every start is a ritual that hides a real problem; if cold cache is the only thing that makes the app build, the resolution problem above is still unsolved.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.