fix(deps): pin react to 19.2.3 to match Expo's exact requirement #147

Merged
ullrich merged 1 commit from align-react-with-expo into main 2026-09-12 04:02:28 +00:00
Owner

The mobile app crashed on startup before any screen mounted:

Incompatible React versions: The "react" and "react-native-renderer"
packages must have the exact same version. Instead got:
  - react:                  19.2.7
  - react-native-renderer:  19.2.3

react-native@0.85.3 (the newest 0.85.x) bundles renderer 19.2.3, and React's runtime check is exact-match, not semver.

Why not upgrade Expo instead

Expo pins react to an exact version in both SDKs, so an SDK upgrade would not have avoided this:

expo react-native react
SDK 56 (current) ~56.0.21 0.85.3 19.2.3
SDK 57 (latest) ~57.0.22 0.86.3 19.2.3

Why the pin is in the catalog

pnpm-workspace.yaml overrides react and react-dom to "catalog:", which forces the version across every workspace package — a pin in apps/mobile/package.json is silently ignored (verified: pnpm install --force still linked 19.2.7). react-dom moves with react because React enforces the same exact-match between them; leaving it at 19.2.7 would relocate the crash to journal and planner.

Why it went unnoticed

apps/mobile/package.json lists react in expo.install.exclude, so expo install --check skips the one package that needed checking. Its 9 suggestions are patch-level Expo drift plus a types-only @types/react bump — none of them fix this.

Also included

  • @trails-cool/i18n react peer >=18>=19 (no React 18 consumer exists)
  • @trails-cool/ui react/react-dom peer aligned to >=19

Verification

  • pnpm typecheck 14/14, pnpm test 12/12, pnpm lint 14/14
  • Mobile app boots on an Android emulator (Pixel 6 Pro, API 34)

🤖 Generated with Claude Code

The mobile app crashed on startup before any screen mounted: ``` Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got: - react: 19.2.7 - react-native-renderer: 19.2.3 ``` `react-native@0.85.3` (the newest 0.85.x) bundles renderer 19.2.3, and React's runtime check is exact-match, not semver. ### Why not upgrade Expo instead Expo pins react to an **exact** version in both SDKs, so an SDK upgrade would not have avoided this: | | expo | react-native | react | |---|---|---|---| | SDK 56 (current) | `~56.0.21` | `0.85.3` | **`19.2.3`** | | SDK 57 (latest) | `~57.0.22` | `0.86.3` | **`19.2.3`** | ### Why the pin is in the catalog `pnpm-workspace.yaml` overrides `react` and `react-dom` to `"catalog:"`, which forces the version across every workspace package — a pin in `apps/mobile/package.json` is silently ignored (verified: `pnpm install --force` still linked 19.2.7). `react-dom` moves with `react` because React enforces the same exact-match between them; leaving it at 19.2.7 would relocate the crash to journal and planner. ### Why it went unnoticed `apps/mobile/package.json` lists `react` in `expo.install.exclude`, so `expo install --check` skips the one package that needed checking. Its 9 suggestions are patch-level Expo drift plus a types-only `@types/react` bump — none of them fix this. ### Also included - `@trails-cool/i18n` react peer `>=18` → `>=19` (no React 18 consumer exists) - `@trails-cool/ui` react/react-dom peer aligned to `>=19` ### Verification - `pnpm typecheck` 14/14, `pnpm test` 12/12, `pnpm lint` 14/14 - Mobile app boots on an Android emulator (Pixel 6 Pro, API 34) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(deps): pin react to 19.2.3 to match Expo's exact requirement
All checks were successful
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CI / Security Scan (pull_request) Successful in 36s
CI / Dockerfile Package Check (pull_request) Successful in 12s
CI / Visual Tests (pull_request) Successful in 2m12s
CI / Checks (pull_request) Successful in 9m40s
CI / Journal Image Smoke Test (pull_request) Successful in 13m22s
CI / E2E Tests (pull_request) Successful in 5m46s
Cancel superseded CI / Cancel in-flight CI (pull_request) Successful in 18s
CD Staging / Tear Down PR Preview (pull_request) Successful in 53s
d6f9ef9c24
The mobile app crashed on startup with "Incompatible React versions:
the react and react-native-renderer packages must have the exact same
version" — react 19.2.7 against the 19.2.3 renderer bundled by
react-native 0.85.3. Nothing rendered; the throw happens in _layout.tsx
before any screen mounts.

Expo pins react to an exact 19.2.3 in both SDK 56 and SDK 57, so
upgrading the SDK would not have avoided this. The catalog's ^19.2.7
conflicted with Expo either way.

The pin has to live in the catalog: pnpm-workspace.yaml overrides
`react` and `react-dom` to "catalog:", which forces the version across
every workspace package, so a per-app pin in apps/mobile/package.json
is silently ignored. react-dom moves with react because React enforces
the same exact-match between them, which would otherwise relocate the
crash to journal and planner.

This went unnoticed because apps/mobile/package.json lists react in
expo.install.exclude, so `expo install --check` skips the one package
that needed checking.

Also tightens @trails-cool/i18n's react peer range to >=19 (the repo has
no React 18 consumer) and aligns @trails-cool/ui's to match.

Verified: journal, planner and mobile all typecheck, test and lint
clean on 19.2.3, and the mobile app boots on an Android emulator.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
trails-cool/trails!147
No description provided.