Skip to content

feat!: require React Native 0.77 or later - #671

Closed
Shahroz16 wants to merge 1 commit into
mainfrom
feat/react-native-peer-floor
Closed

Shahroz16 wants to merge 1 commit into
mainfrom
feat/react-native-peer-floor

Conversation

@Shahroz16

@Shahroz16 Shahroz16 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Not planned under the current support policy — kept as a draft for reference. (Policy: keep minimums as low as actually works and leave peerDependencies at * with no enforcement major. The README in #670 documents RN 0.77 as the minimum that works, tested end to end from 0.81.)

Blocked on the support decision (MBL-2389) — do not merge.

RN SDK 7.0 candidate. The support matrix recommends 0.81 as the supported floor (stock template, full-app CI), with 0.77–0.80 as a conditional path (module builds only, upgraded Android tooling), enforced only in 7.0. Which number this range carries, >=0.77.0 as drafted or >=0.81.0, is Shahroz's decision.

Refs MBL-2389

Change

react stays "*"; nothing here needs a newer React than the one each React Native release pins.

Why 0.77

The package already doesn't work below 0.77. "*" only moves the failure from npm install to a native build:

  • Android (sets the floor): android/build.gradle applies org.jetbrains.kotlin.plugin.compose from compose-compiler-gradle-plugin:$kotlinVersion, using the host's kotlinVersion. That plugin exists only for Kotlin 2.0+. The RN 0.76 template ships kotlinVersion = "1.9.24"; RN 0.77 ships "2.0.21".
  • Codegen: module specs declare EventEmitter<…> events (NativeCustomerIO, NativeCustomerIOLogging, NativeCustomerIOMessagingInApp). Codegen support starts in RN 0.76.
  • iOS doesn't raise it further: codegenConfig.ios.modulesProvider is only read from RN 0.79 (generate-artifacts-executor.js). But every iOS TurboModule also registers through RCT_EXPORT_MODULE() + getTurboModule:, so 0.77/0.78 still find them. componentProvider is read from 0.77. The podspec uses min_ios_version_supported (15.1 on 0.76+).
  • Android RN APIs used (BaseReactPackage, generated emitOn…, ViewManagerDelegate) all exist by 0.76.

Builds (stock templates from @react-native-community/cli init, packed SDK, :app:assembleDebug)

React Native Result with 6.13.1
0.76.9 ❌ Could not find org.jetbrains.kotlin:compose-compiler-gradle-plugin:1.9.25
0.77.3 as generated ❌ checkDebugAarMetadata: androidx.core:core(-ktx):1.17.0 needs compileSdk 36 and AGP ≥ 8.9.1
0.77.3 + compileSdk 36, buildTools 36.0.0, AGP 8.9.1, Gradle 8.11.1 ✅
0.81.6 as generated ✅
0.88.0 as generated ✅

So 0.77 is the lowest React Native that can work. But since 5.1.0, when native Android 4.13.0 moved to AndroidX Core 1.17, a React Native app also needs compileSdk 36 and AGP 8.9.1+. Stock templates first ship that in 0.81. The docs PR (#670) documents the edits needed on 0.77–0.80.

For MBL-2389: >=0.77.0 lets the conditional 0.77–0.80 path install. >=0.81.0 would match the recommended supported floor (builds as generated). Either way, the peer range can't express the compileSdk/AGP requirement.

Deep-link scene shim (MBL-2395 add-on): stays dynamic

ios/wrappers/CustomerIOReactNativeDeepLinkRouter.swift checks at runtime that RCTLinkingManager responds to scene:openURLContexts: before enabling scene routing. That method first appears in RCTLinkingManager.h in React Native 0.88.0; it's absent in 0.76.0, 0.77.0, 0.78.0, 0.83.10, 0.86.3 and 0.87.1. A 0.77 floor doesn't guarantee it, so the shim stays as is. It could become a direct call only with a 0.88 floor.

⚠ Trade-off for MBL-2389: prereleases (why not >=0.77.0-0)

npm only matches a prerelease against a range that names the same x.y.z. So with any real range, an npm app on a React Native RC or nightly fails npm install with ERESOLVE unless it passes --legacy-peer-deps. That includes an Expo canary that pins one.

Appending -0 doesn't help. semver.satisfies results:

Version >=0.77.0 >=0.77.0-0
0.77.0-rc.1 ❌ ✅
0.88.0-rc.6 ❌ ❌
0.89.0-nightly-20261020-f1f516e8c ❌ ❌
0.88.0 ✅ ✅

npm 11 install test: a >=5.0.0-0 peer still rejects typescript@7.1.0-dev with ERESOLVE. Only "*" accepts prereleases, because npm special-cases it; even plain semver.satisfies rejects them for *. No npm range expresses "0.77 or later, including future prereleases".

This is npm-specific: Yarn and pnpm match prereleases against peer ranges and only warn on mismatches.

If prerelease installs must keep working on npm, the alternative is to keep "*" and enforce the floor at build time (a podspec/Gradle check against the host's React Native version, with a readable message).

CI note

This PR now changes only package.json and the lockfile, so the scene E2E lane doesn't run on it. The earlier scene failure on this PR was the iPhone 17 Pro pin fixed by #668.

Breaking change

Apps on React Native < 0.77 (already unbuildable on Android) and apps on React Native prereleases get an npm ERESOLVE at install. Ships as a semver-major per feat!.

Codex review

Codex (gpt-6.1-sol, high) reviewed this diff, with nothing actionable on the changed lines:

  • It found no RN API newer than 0.77 in ios/wrappers/**, the podspecs, android/src/** or src/**. That covers RCT_EXPORT_MODULE + getTurboModule (so modulesProvider doesn't force 0.79), BaseReactPackage, ViewManagerDelegate, UIManagerHelper, deep imports and TurboModule lookup. Keeping react: "*" is fine. The range and lockfile match.
  • Corrected two statements in this description: Yarn and pnpm do match prereleases (the ERESOLVE is npm-specific), and an Expo canary only hits it when it pins a prerelease React Native.
  • Noted that --legacy-peer-deps skips all peer handling for the scene install. The bypass is gone now: the host moves to RN 0.88.0 in ci(e2e): stop pinning the scene E2E simulator model #668.
  • A cross-repo review suggested >=0.77.0-0 to admit prereleases. Rebutted with the table above: it only admits 0.77.0's own prereleases.
  • Raised the Android toolchain gap (AndroidX Core 1.17 → compileSdk 36 / AGP 8.9.1). The builds above confirmed it.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Sample app builds 📱

Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request.


Builds are in progress. This comment will be updated when they finish.

  • iOS APN: 671.1.0 (29875599)
  • Android APN: 671.1.0 (29875599)

The package declared `react-native: "*"` while it already needs 0.77+: it is
New Architecture only since 6.0.0, codegen EventEmitter module events need
0.76+, and the Android module applies the Compose compiler plugin with the
host's Kotlin version, which needs Kotlin 2.0 (first used by the 0.77
template; 0.76 ships 1.9.x and fails to resolve the plugin). Declare that
floor so npm reports it at install time instead of a Gradle failure.

BREAKING CHANGE: `react-native` peer dependency is now `>=0.77.0`. npm 7+
fails installs on older React Native, and on React Native prereleases
(npm only matches a prerelease against a range naming the same x.y.z)
unless run with --legacy-peer-deps.

Refs MBL-2389

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Shahroz16

Copy link
Copy Markdown
Contributor Author

Closing: not planned. We're keeping the react-native peer range at * and documenting the real minimum instead (see #670).

@Shahroz16 Shahroz16 closed this Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant