Skip to content

Update react-native to v0.87.0 and jest to v30 - #73

Merged
dfalling merged 1 commit into
mainfrom
update-dependencies
Aug 17, 2026
Merged

Update react-native to v0.87.0 and jest to v30#73
dfalling merged 1 commit into
mainfrom
update-dependencies

Conversation

@dfalling

Copy link
Copy Markdown
Owner

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)

JSView/TextInput are function components now, so those names no longer double as their instance types. Refs move to the ViewInstance/TextInputInstance aliases 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 kotlin extension and hard-rejects the -dontoptimize default proguard file, so this takes the same android.builtInKotlin=false / android.newDsl=false / proguard-android-optimize.txt opt-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-native and react-native-screens move to their latest in-range releases; the pinned versions fail to compile against 0.87 (Function invocation 'getReactTag()' expected and a Bitmap? 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 raw src/*.tsx, so the library's own errors surfaced as ours. tsc now reads its published declarations; Metro still bundles from src/, so runtime is unchanged. Revert once view-shot supports 0.87.

jest 30 (#42)

@react-native/jest-preset 0.87 still declares jest-environment-node, babel-jest and @jest/create-cache-key-function at ^29.7.0. Its testEnvironment extends whichever jest-environment-node resolves beside the preset, so a v29 environment got paired with jest-runtime 30 and all 7 suites died on this._moduleMocker.clearMocksOnScope is not a function. An overrides block forces those three to v30; drop it when RN's preset targets jest 30.

babel 8 (#55) — not merged

@react-native/babel-preset drives the entire RN transform and its plugin chain asserts ^7.0.0-0. Under @babel/core 8 it throws Requires 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/core 7, so a top-level v8 never actually drives a transform — it only shadows them. Held at v7 in renovate.json until React Native moves to Babel 8.

Worth knowing: this only reproduces on a clean install. An incremental bun install can leave a nested @babel/core 7 under @jest/transform that masks it — which is why the original #55 run failed differently (loadPartialConfig expects a callback).

That investigation also turned up @babel/plugin-transform-export-namespace-from, which babel.config.js applies directly but was only ever installed transitively via @babel/preset-env. Now declared explicitly.

Gradle ceiling

The <9.4 pin in renovate.json moves 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, clean assembleDebug (APK produced), and a --dev false release bundle — all from a fresh bun install --frozen-lockfile.

🤖 Generated with Claude Code

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>
@dfalling
dfalling merged commit 74511bb into main Aug 17, 2026
2 checks passed
@dfalling
dfalling deleted the update-dependencies branch August 17, 2026 11:28
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