Update react-native to v0.87.0 and jest to v30 - #73
Merged
Conversation
Renovate raised react-native 0.87 (#61) and jest 30 (#42) separately, but neither can land alone: jest 30 needs overrides that only make sense alongside RN's preset, and RN 0.87 needs the same Android toolchain bump that jest is indifferent to. Combined so CI can go green on one commit. react-native 0.87 - `View`/`TextInput` are function components now, so those names no longer double as their instance types. Refs move to the dedicated `ViewInstance`/`TextInputInstance` aliases RN 0.87 exports for exactly this. - AGP 9.2.1 comes with RN 0.87 and wants Gradle >= 9.4.1, so the wrapper goes to 9.4.1 — the version RN's own gradle-plugin ships. AGP 9 also registers its own `kotlin` extension and rejects the `-dontoptimize` default proguard file; both need the opt-outs RN 0.87's template uses. - kotlinVersion 2.2.0 matches the Kotlin the RN gradle-plugin is built with, which is what lets Gradle 9.4's bundled stdlib be read at all. - maplibre-react-native and react-native-screens are moved to their latest in-range releases; the pinned ones fail to compile against 0.87 (`getReactTag()` and a `Bitmap?` nullability error). - react/react-test-renderer stay at 19.2.3 — still the renderer version bundled in react-native, checked per renovate.json's instructions. - view-shot 5.1.1 has no 0.87 support yet and is typechecked from source via its "react-native" export condition, so tsc reads its published declarations instead. Metro still bundles from src/, so runtime is unchanged. jest 30 - @react-native/jest-preset 0.87 still asks for jest-environment-node, babel-jest and @jest/create-cache-key-function at ^29.7.0. Its testEnvironment extends whichever jest-environment-node sits next to the preset, so a v29 environment met jest-runtime 30 and every suite died on `clearMocksOnScope is not a function`. Overridden to v30. Babel 8 (#55) is not included: @react-native/babel-preset's plugin chain asserts `^7.0.0-0`, so under @babel/core 8 it throws before any suite runs. Metro, babel-jest and the preset each vendor their own @babel/core 7, so a top-level v8 only shadows them — held at v7 in renovate.json until RN moves. That also pins @babel/plugin-transform-export-namespace-from, which babel.config.js applies directly but only had a transitive install. The Gradle ceiling in renovate.json moves 9.4 -> 9.7 for the same reason it existed: Gradle 9.7 bundles Kotlin 2.4.0 and RN's 2.2.0 plugin reads metadata only to 2.3.0. 9.5.1 and 9.6.1 were verified to configure cleanly. Verified: tsc, biome, 51 tests, clean `assembleDebug`, and a `--dev false` release bundle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Combines and fixes the failing dependency PRs. Closes #61, closes #42, closes #55.
Why these are combined
Neither RN 0.87 nor jest 30 can land alone: jest 30 only works with overrides against RN's preset, and RN 0.87 drags in an Android toolchain bump. One commit, one green CI.
react-native 0.87 (#61)
JS —
View/TextInputare function components now, so those names no longer double as their instance types. Refs move to theViewInstance/TextInputInstancealiases RN 0.87 exports for exactly this case.Android — AGP 9.2.1 arrives with RN 0.87 and requires Gradle >= 9.4.1, so the wrapper goes to 9.4.1 (the version RN's own gradle-plugin ships). AGP 9 additionally registers its own
kotlinextension and hard-rejects the-dontoptimizedefault proguard file, so this takes the sameandroid.builtInKotlin=false/android.newDsl=false/proguard-android-optimize.txtopt-outs as RN 0.87's template.kotlinVersion-> 2.2.0 to match the Kotlin the RN plugin is compiled with.Third-party —
@maplibre/maplibre-react-nativeandreact-native-screensmove to their latest in-range releases; the pinned versions fail to compile against 0.87 (Function invocation 'getReactTag()' expectedand aBitmap?nullability error).react / react-test-renderer stay at 19.2.3 — still the renderer version bundled in react-native, checked with the command in
renovate.json.react-native-view-shot 5.1.1 has no 0.87 support yet, and its
"react-native"export condition points tsc at rawsrc/*.tsx, so the library's own errors surfaced as ours. tsc now reads its published declarations; Metro still bundles fromsrc/, so runtime is unchanged. Revert once view-shot supports 0.87.jest 30 (#42)
@react-native/jest-preset0.87 still declaresjest-environment-node,babel-jestand@jest/create-cache-key-functionat^29.7.0. ItstestEnvironmentextends whicheverjest-environment-noderesolves beside the preset, so a v29 environment got paired with jest-runtime 30 and all 7 suites died onthis._moduleMocker.clearMocksOnScope is not a function. Anoverridesblock forces those three to v30; drop it when RN's preset targets jest 30.babel 8 (#55) — not merged
@react-native/babel-presetdrives the entire RN transform and its plugin chain asserts^7.0.0-0. Under@babel/core8 it throwsRequires Babel "^7.0.0-0", but was loaded with "8.0.1"before any suite runs. Metro, babel-jest and the preset each vendor their own@babel/core7, so a top-level v8 never actually drives a transform — it only shadows them. Held at v7 inrenovate.jsonuntil React Native moves to Babel 8.Worth knowing: this only reproduces on a clean install. An incremental
bun installcan leave a nested@babel/core7 under@jest/transformthat masks it — which is why the original #55 run failed differently (loadPartialConfigexpects a callback).That investigation also turned up
@babel/plugin-transform-export-namespace-from, whichbabel.config.jsapplies directly but was only ever installed transitively via@babel/preset-env. Now declared explicitly.Gradle ceiling
The
<9.4pin inrenovate.jsonmoves to<9.7, for the same reason it existed: Gradle 9.7 bundles Kotlin 2.4.0 and RN 0.87's plugin (Kotlin 2.2.0) reads metadata only to 2.3.0. 9.5.1 and 9.6.1 were verified to configure cleanly, so renovate can offer them as ordinary PRs.Verification
tsc --noEmit,biome check, 51/51 tests, cleanassembleDebug(APK produced), and a--dev falserelease bundle — all from a freshbun install --frozen-lockfile.🤖 Generated with Claude Code