Skip to content

[WIP] feat: enable React Native new architecture - #7465

Open
janicduplessis wants to merge 38 commits into
developfrom
@janic/enable-new-arch
Open

[WIP] feat: enable React Native new architecture#7465
janicduplessis wants to merge 38 commits into
developfrom
@janic/enable-new-arch

Conversation

@janicduplessis

Copy link
Copy Markdown
Contributor

Stacked on #7366 (RN 0.81 upgrade). Do not merge until #7366 lands. This re-opens #7463, which auto-closed when its temporary side-PR base branch was deleted.

Description

#7366 upgrades to RN 0.81 with newArchEnabled=false on both platforms — the upgrade and the architecture flip were deliberately separated. This PR is the second half: it flips the new architecture on (Fabric + TurboModules) and brings in the source/native changes from the abandoned WIP attempt in #6924 that are still required to make the app build and run under Fabric on RN 0.81.6.

Every change in the diff is in service of that goal. Where #6924 contained incidental drift (it was based on a much older develop), that drift has been left out — only the new-arch-required pieces remain.

Solution

The three architecture switches live in android/gradle.properties (newArchEnabled=true), the iOS Podfile (drops the explicit :new_arch_enabled => false so use_react_native! defaults to true on 0.81), and the per-target Info.plist files (RCTNewArchEnabled=true). The main iOS target and MainApplication.kt also opt into the experimental release level since Fabric on 0.81 is still gated as experimental.

A handful of libraries don't work on Fabric and have to be swapped:

  • react-native-reanimated 3.19.5 → 4.3.1. Reanimated v3 is old-arch-only; v4 is new-arch-only. v4 splits its runtime into a companion package react-native-worklets@0.8.3, so the Babel plugin moves from react-native-reanimated/plugin to react-native-worklets/plugin and Jest's transformIgnorePatterns lets it through the transformer. A patch (patches/react-native-reanimated+4.3.1.patch) carries one local fix.
  • react-native-text-input-maskreact-native-advanced-input-mask@1.4.6. react-native-text-input-mask has no Fabric support. We attempted this swap previously on the old architecture and backed it out because of regressions, so the two callsites (ExchangeInput and GweiInputPill) need careful review — see the test plan.
  • @react-native-masked-view/masked-viewrainbow-me/masked-view fork (#new-arch branch). Upstream's Fabric implementation was merged (#221) and then reverted (#222) for a lint failure; the reintroduce PR (#223) was closed and the package overhaul (#246) is paused waiting for an RN-native mask attribute that @paradowstack estimates will ship in RN 0.85. RN is on 0.81 today, so the fork is the bridge until then. The fork is the previously-reverted calculate native price from market details of selected swap input #221 implementation rebased onto develop.

The RN cornerCurve patch needs to move too: #7366 patches RCTView.m on the legacy ObjC path to force kCACornerCurveContinuous, but Fabric doesn't go through that code. The patch is rewritten to flip the default BorderCurve resolved value from Circular to Continuous in BaseViewProps.cpp, which is the equivalent place in the Fabric render path.

Two of rainbow's own native components had to be ported to Fabric. The Zoomable Button (used by ButtonPressAnimation on iOS) was a RCTViewManager-based view in ios/Button.swift + ButtonManager.m. It's replaced by a codegen'd Fabric component: a JS spec at src/codegen/specs/NativeButtonNativeComponent.ts, an Objective-C++ implementation at src/codegen/ios/Components/Button/ButtonComponentView.mm, and a podspec (src/codegen/rainbow-codegen.podspec) that ties it into the iOS build. ButtonPressAnimation.ios.tsx now consumes the codegen'd component directly, and the NativeButton.tsx wrapper around requireNativeComponent('RNZoomableButton') is gone. The same migration is done for react-native-cool-modals: RNCMScreen, RNCMScreenStack, and WindowPortal move from RCTViewManager + RNCMScreen.m/RNCMScreenStack.m to Fabric component views with ShadowNodes, ComponentDescriptors, and a dedicated touch handler. A react-native-cool-modals.podspec and specs/Native*.ts make the package participate in codegen.

Finally, a few cleanups become possible once everything is on Fabric: src/components/AnimatedComponents/extendedPropsAllowlists.ts and the pickAnimatedProps plumbing it fed are deleted (Fabric handles prop forwarding without an allowlist), the iOS bridging header / project.pbxproj lose entries for the deleted Objective-C files, and a .clang-format is added for the new Objective-C++ files.

#6924 also swapped the haptics library from react-native-turbo-haptics to react-native-haptic-feedback. That swap is dropped here — develop has since standardized on react-native-turbo-haptics, which already supports the new architecture.

Test plan

Local builds haven't been run yet (TODO before un-drafting). When they have:

  • iOS: bundle exec pod install succeeds, app builds and launches with RCTNewArchEnabled=true.
  • Android: yarn android builds and launches with newArchEnabled=true in gradle.properties.
  • Reanimated 4 worklets run end-to-end: swap sheet animations, bottom sheets, rainbow toast.
  • ButtonPressAnimation taps feel identical to before — the codegen'd Fabric button should be visually indistinguishable from the old RCTViewManager button.
  • Cool modal stack still opens, dismisses, and gesture-closes; the WindowPortal still passes touches through.
  • Masked input — ExchangeInput and GweiInputPill (high-scrutiny because the prior old-arch attempt regressed): typing, paste, autofocus, IME composition on Android, backspacing across the mask boundary, focus/blur side effects.
  • Masked-view fork doesn't regress profile sheet / cards layout.
  • DApp browser loads and renders a page (Fabric webview).
  • No Hermes crashes at startup.

Upgrades react-native 0.79.5 → 0.81.6, react 19.0.0 → 19.1.5,
expo SDK 53 → 54, CLI 18 → 20, babel-preset/metro-config 0.79 → 0.81.

Native changes:
- Android: compile/targetSdk 35 → 36, buildTools 36.0.0, Gradle 8.14.3,
  MainApplication.kt uses loadReactNative(), AndroidManifest has
  usesCleartextTraffic, adds edgeToEdgeEnabled=false.
- iOS: removes :hermes_enabled / :new_arch_enabled from use_react_native!
  (no longer accepted by 0.81).

Config:
- metro.config.js: metro-config/src → metro-config/private/defaults,
  drops unstable_enablePackageExports override.
- src/debugging/network.js: XHRInterceptor moved under devsupport/devmenu.

Patches:
- react-native: recreated for 0.81.6 with only the cornerCurve continuous
  fix (old-arch ObjC path); accessibility fix was upstreamed.
- expo-linear-gradient: renamed to 15.0.8, content still applies.

Keeps newArchEnabled=false.
…iles

Pod install output for RN 0.81 + Expo 54. The :new_arch_enabled keyword
is still accepted by use_react_native! in 0.81 but defaults to true
when omitted, so it needs to be passed explicitly to opt out.

Also regenerates Podfile.lock and picks up Expo 54 template changes
to Info.plists (RCTNewArchEnabled=false, alphabetical key order) and
project.pbxproj (input/output paths on the Expo Configure project
script phase).
patch-package failed to parse the previous hunk — the header line
counts were off and blank context lines had no leading space, which
patch-package's parser treats as end-of-hunk.
RN 0.81 no longer exposes $REACT_NATIVE_PATH to the Xcode build env,
so the old Bundle React Native script phase broke with
"react-native-xcode.sh: No such file or directory". The sentry-xcode.sh
wrapper already invokes react-native-xcode.sh internally, so pass it
directly under with-environment.sh without the extra argument layer.
Metro's unstable_enablePackageExports now defaults to true in RN 0.81.
Zustand's ESM entry points use import.meta, which Hermes does not
support — causing SyntaxError 'import.meta' is currently unsupported
at store creation (createRainbowStore, createQueryStore).

Add explicit mappings under the package.json react-native field to
force Metro to resolve zustand and its subpaths to the CJS files,
bypassing the exports map.
…e export condition

With zustand@5.0.10, the package's own exports map routes the
`react-native` condition to the CJS entry (`./index.js`), so Metro no
longer resolves to the ESM files that used import.meta. The manual
subpath overrides added in 592d73b to work around v4's exports map
are no longer needed.
v1.2.3 overrode setHitSlopRect and referenced removed RN APIs; v2.0.0
drops those for RN 0.80+. Patch file renamed to match the new version
— contents are effectively the same minus line-number shifts.
…for RN 0.81

- flash-list: BlankAreaEvent.dispatch references eventData directly,
  but in RN 0.81's Event<T> base class eventData is protected and
  must be accessed via getEventData(). New patch file.
- react-native-performance: PerformanceMarkerManager's
  getExportedCustomDirectEventTypeConstants() overrode the base's
  MutableMap<String, Any> return type. In RN 0.81 the base changed
  to Map<String, Any> (covariant narrowing). Updated the patch to
  match.

Both hunks ported from #6924.
react-native@0.81.6 ships react-native-renderer built against
react@19.1.4. Pinning react to 19.1.5 caused a runtime crash:

  Incompatible React versions: The 'react' and 'react-native-renderer'
  packages must have the exact same version. Instead got:
  - react: 19.1.5
  - react-native-renderer: 19.1.4

Downgrading one patch matches the bundled renderer and resolves it.
The internal react-native-sandbox repo has its own RN 0.81 branch. Set
SANDBOX_BRANCH so rainbow-scripts' iOS / Android prebuild hooks check
that out instead of the default branch — needed until the sandbox
branch is merged. Applied to ios-e2e, android-e2e, ios-builds (both
install steps), and unit-test workflows.
…on drift

Two Android startup crashes on RN 0.81 caused by libraries reflecting on
internal RN Java fields that were renamed in 0.81's Kotlin rewrite:

- Bump react-native-animateable-text 0.16.0-beta.0 → 0.17.1 to pull in RN
  0.81 + old-arch support. The pre-bump build crashed at first text view
  creation:
    java.lang.NoSuchFieldException: No field mPreparedSpannableText in
    class Lcom/facebook/react/views/text/ReactTextShadowNode;
        at com.reactnativereanimatedtext.JBTextShadowNode.<clinit>
  RN 0.81 renamed the spannable text field; v0.17.1 reads the new one.

- Bump react-native-reanimated 3.19.4 → 3.19.5 (latest 3.x). Picks up
  bridge-lifecycle fixes that resolve a -Xcheck:jni abort in debug
  builds (NativeProxyCommon.requestRender called on null after JS thread
  bounces).

Verified locally with `yarn android` on Pixel 6 emulator + RN 0.81.6 +
Hermes — Wallet Screen renders cleanly after both bumps.

Note: the in-repo InternalModule.java reflection on
NativeModuleRegistry.mModules / ModuleHolder.mModule also needs renaming
to moduleMap / module respectively for SafeWebSocketModule to install on
RN 0.81. Filed separately since the stub committed on develop differs
from the runtime version.
RN 0.81's Metro respects package.json `exports`, routing static `import`
statements to the `.import` condition. `@reservoir0x/reservoir-sdk`
ships a Parcel-bundled `.mjs` there whose mangled named exports
(e.g. `$6496f8ee40d0c4eb$export$5d730b7aed1a3eb0 as createClient`) come
through as `undefined` once Hermes loads the release bundle.

The crash showed up on every cold launch:

    [TypeError: Cannot read property 'createClient' of undefined]
        at initializeReservoirClient

…which kills the app before the welcome screen mounts, breaking every
E2E test (iOS sim and Android emulator both — same JS root cause). Local
debug builds happen to work because Metro serves a different transform
in dev mode.

Override the resolver for this one package to its CJS file (`./dist/index.js`,
its `main` field). No effect on other packages.
`require.resolve('@reservoir0x/reservoir-sdk/dist/index.js')` and even
`require.resolve('@reservoir0x/reservoir-sdk/package.json')` throw
`ERR_PACKAGE_PATH_NOT_EXPORTED` on Node 22+ because the package's
`exports` field only exposes `.` (no subpath, no `./package.json`).

CI bundle step crashed in `metro.config.js` evaluation, blowing up
`Test Build` before the bundle could be produced.

Compute the path off `__dirname` instead — metro.config.js sits at the
workspace root, so `node_modules/@reservoir0x/reservoir-sdk/dist/index.js`
is the deterministic path. Bypasses Node's exports check entirely.
Rock's remote-build cache fingerprints native files + package.json by
default but not `metro.config.js`. Yesterday's reservoir-sdk resolveRequest
override never reached CI builds — rock kept serving the older cached
`.app` (fingerprint `188295d8...` — pre-fix) because nothing in the
fingerprint changed.

Add metro.config.js, metro.transform.js, and babel.config.js to
extraSources so JS-bundle config changes properly invalidate the cache.
RN 0.81's Metro defaults `unstable_enablePackageExports` to true, which
routes static `import` statements to packages' `import` condition. Many
npm libs ship ESM there (often Parcel-mangled, e.g. @reservoir0x/reservoir-sdk
and any transitive `class X extends Y` deps); when bundled for Hermes,
their named exports come through as `undefined` and the app crashes on
launch:

    [TypeError: Cannot read property 'prototype' of undefined]
    [TypeError: Cannot read property 'createClient' of undefined]
        at initializeReservoirClient
        at Root (App.tsx)

…which kills the app before the welcome screen mounts and breaks every
E2E + Perf test on iOS sim and Android (real device + emulator).

Disabling package exports makes Metro fall back to the `main` field
(CJS), matching what RN 0.79 effectively did and what these packages
historically test against. Replaces the per-package reservoir-sdk
override from b05b3d5 / 96e5f1a with a single config flag.
Replaces the global `unstable_enablePackageExports: false` from d50271b
(too broad — flips behaviour for every package) with a per-package map of
`exact moduleName → CJS filepath` overrides in `metro.config.js`.

The RN 0.81 Metro / Hermes interaction crashes on launch when packages
ship Parcel-mangled ESM via the `import` condition: their named exports
come through as `undefined` in the release bundle. Reservoir-sdk was the
visible top-of-stack symptom; the underlying `prototype of undefined`
fires deeper in viem (transitively required by reservoir's CJS — but
viem itself shares the same export shape).

Add packages to `FORCE_CJS_PACKAGES` as we find them. If this list grows
much further, reconsider the global flag.
These three packages all ship parallel `_esm/*` and `_cjs/*` (or
`dist/esm`/`dist/cjs`) directories with mirrored `import`/`default`
conditions. Rainbow imports a handful of subpaths directly (e.g.
`viem/accounts`) and many more transitively through reservoir-sdk and
WalletConnect, so listing each subpath was unworkable.

Replace the static map with a prefix-matched override that resolves
each request to the package's `default` (CJS) export by reading the
package's `package.json` exports field directly. Bypasses Node 22's
strict subpath enforcement by joining against the package root rather
than re-resolving the subpath through `require.resolve`.

Eliminates 22 ESM module duplicates from the production Metro bundle.
Reservoir-sdk's CJS calls `axios.create()` at top-level, which fires the
moment reservoir-sdk loads. Axios's exports map gates the browser-safe
build behind a `browser` condition that Metro doesn't add on native
platforms (only on `web`), so Metro picks `dist/node/axios.cjs` —
which `require()`s `http`, `https`, `url`, `stream`, `zlib`, etc.

Those Node builtins resolve to undefined-or-stub in the bundle, and
their absence cascades into `Cannot read property 'prototype' of
undefined` during axios load — manifesting as a launch crash that
short-circuits reservoir-sdk's exports and produces the secondary
`createClient of undefined` error users see.

Pin `axios` to its browser CJS through FORCE_CJS_PACKAGES; the bundle
drops from 24 axios deps to 10 (Node builtins eliminated).
…N 0.81

`RNFSManager.reject(...)` calls `promise.reject(null, ex.getMessage())`
for the generic exception path. RN 0.81's `PromiseImpl.reject(code,
message)` is annotated `@NonNull` for `code` and trips a
`java.lang.NullPointerException: Parameter specified as non-null is
null` the moment it's invoked — currently fires on `unlink` during
the wallet-creation flow and crashes the app.

Pass an "ERROR" code plus the original throwable instead.
The launch crash root-caused to axios picking its Node CJS entry
(which require()s http/https/url/stream/zlib). axios 1.10.0+ added
a `react-native` export condition that maps to the browser CJS, so
forcing axios to ^1.16.0 via yarn resolutions lets Metro's normal
resolver pick the right entry.

With axios fixed, the speculative viem/ox/abitype prefix overrides
and the reservoir-sdk single-file override are no longer needed.
Strip the custom FORCE_CJS_PACKAGES map and the resolveToCjs helper
entirely; resolveRequest now just delegates to Metro and falls back
through the existing recovery chain.

axios in the production bundle is now v1.16.0 from
`dist/browser/axios.cjs`, with the Node-only deps eliminated.
The hand-rolled `diff -u` patch didn't include the `diff --git` header
that patch-package requires to locate the target file, so CI failed
postinstall with "ERROR: Failed to apply patch for package react-native-fs".

Re-emit the same one-line edit (RNFSManager.reject null code → "ERROR")
in patch-package's expected format (`diff --git a/... b/...` + index +
function-context hunk header). Verified against a fresh node_modules
install of react-native-fs@2.16.6.
PR #7396 bumped @shopify/flash-list to 1.8.3, which carries the
BlankAreaEvent.kt fix our patch was applying. patch-package was
already skipping our 1.8.2-named patch because the installed
version no longer matches; remove the dead file.
… 0.81

On RN 0.81, an Android TextInput with bold weight and size 18 stops
honoring includeFontPadding:false once it has content — measured height
jumps from ~28px (placeholder-only) to ~40px (with text), making the
Send recipient row reflow as the user types. The placeholder path still
respects the flag, so only the populated state regresses.

Lock the input to the RN 0.79 measurement (28px ≈ 18px font + ~10px
Android ascender/caret padding) so both states render identically.
The fingerprint controls only the native build cache; `rock bundle`
runs every CI build and produces a fresh JS bundle regardless.
Adding metro.config.js / metro.transform.js / babel.config.js to
extraSources doesn't keep stale bundles out of CI — the original
"stale APK" symptom that motivated this addition was actually a
silent fall-through in the metro resolver after a Node 22
ERR_PACKAGE_PATH_NOT_EXPORTED error, fixed in 96e5f1a.

Restore extraSources to the develop state.
iOS:
- Restore OpenInRainbow / ShareWithRainbow Info.plist to develop (last
  pod-install run reformatted these from 2-space indent to tabs; pure
  whitespace, no semantic change).
- Trim Rainbow/Info.plist back to the single RCTNewArchEnabled=false
  addition required by RN 0.81's bundle main info dictionary read in
  RCTUtils.mm. Remove four alternate-app-icon entries (baggy, chonks,
  mog, redacted) that came in via the local internals prebuild hook
  during the same pod-install dance and don't belong in this PR.

Patches:
- Drop patches/react-native-reanimated+3.19.4.patch — package.json now
  pins 3.19.5 which ships makeSynchronizable / Synchronizable upstream
  (verified in node_modules ReanimatedModuleProxySpec.{cpp,h}).
  Patch was already a no-op (filename version mismatch makes
  patch-package skip it).
- Drop patches/react-native-animateable-text+0.16.0-beta.0.patch —
  package.json now pins 0.17.1, which ships the `children`-allowed
  AnimateableTextProps shape upstream (verified in node_modules
  src/TextProps.tsx and lib/typescript/src/index.d.ts). Same no-op
  status pre-deletion.
Add `# PATCH CONTEXT` headers to the six patches affected by the RN
0.81 work, per the Patch Management RFC. Each header captures Why,
Upstream Issue, Linear Issue, and Remove when so the patch is self-
documenting and tracking is anchored to a Linear ticket.

- react-native+0.81.6.patch — FEPLAT-97
- react-native-fs+2.16.6.patch — FEPLAT-93
- @Shopify+react-native-performance+4.1.2.patch — FEPLAT-94
- expo-linear-gradient+15.0.8.patch — FEPLAT-96
- @react-native-menu+menu+2.0.0.patch — FEPLAT-95
- react-native-animateable-text+0.17.1.patch — FEPLAT-98 (new)

The animateable-text patch was deleted in d3b7aff on the
assumption that the upstream fix in 0.17.1 was complete. Source was
fixed but the published `lib/typescript/src/*.d.ts` still ship the
old `Omit<TextProps, "children">` shape, so consumers fail
TypeScript with TS2322 on `<AnimatedText>{children}</AnimatedText>`
(reproduced by `yarn lint:ci` on
src/design-system/components/Text/AnimatedText.tsx). Restore the
patch against 0.17.1's distributed types until upstream republishes.

ios/Podfile.lock: pick up animateable-text 0.16.0-beta.0 → 0.17.1
(was already bumped in package.json/yarn.lock; lockfile was stale).
The previous patch only updated lib/typescript/src/TextProps.d.ts and
AnimateableText.web.d.ts, but `package.json#types` points to
lib/typescript/src/index.d.ts — which carries the same un-regenerated
`Omit<TextProps, "children">` shape and is what TypeScript actually
consumes when resolving the package. Local lint passed only because I
had hand-edited that file during exploration; CI hit pristine state and
TS2322 stuck.

Add the third hunk so all three published .d.ts files are aligned with
the upstream source. lint:ts now runs clean against pristine
node_modules.
@socket-security

socket-security Bot commented May 11, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: Arbitrary code execution in npm protobufjs

CVE: GHSA-xq3m-2v4x-88gg Arbitrary code execution in protobufjs (CRITICAL)

Affected versions: >= 8.0.0 < 8.0.1; < 7.5.5

Patched version: 7.5.5

From: ?npm/@react-native-firebase/app@23.8.8npm/protobufjs@7.5.4

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/protobufjs@7.5.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@janicduplessis janicduplessis changed the title feat: enable React Native new architecture [WIP] feat: enable React Native new architecture May 11, 2026
@janicduplessis
janicduplessis marked this pull request as ready for review May 11, 2026 17:58
Comment thread ios/Podfile.lock
- Firebase/Core (12.10.0):
- React-RCTFabric
- ReactCommon/turbomodule/core
- FBLazyVector (0.81.5)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Podfile.lock downgrades React Native from 0.81.6 to 0.81.5

High Severity

The committed Podfile.lock reverts all React Native pods from 0.81.6 to 0.81.5, but package.json declares "react-native": "0.81.6". This means pod install would resolve to native code from a different RN version than what the JS bundle expects, risking build failures or runtime crashes from native/JS ABI mismatches. The lockfile appears to have been generated from a stale node_modules state.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ccc63ae. Configure here.

Comment thread ios/Podfile.lock
RNPermissions:
:path: "../node_modules/react-native-permissions"
RNReactNativeHapticFeedback:
:path: "../node_modules/react-native-haptic-feedback"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Podfile.lock adds undeclared react-native-haptic-feedback dependency

Medium Severity

The Podfile.lock introduces RNReactNativeHapticFeedback (from react-native-haptic-feedback), but package.json has no such dependency. The PR description explicitly states the haptics swap to react-native-haptic-feedback was "dropped here" in favor of the existing react-native-turbo-haptics. This phantom pod entry confirms the lockfile was generated from a different branch state and would fail pod install since the package isn't in node_modules.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ccc63ae. Configure here.

Comment thread ios/Podfile.lock
- SocketRocket
- Yoga
- RNReanimated/reanimated/apple (3.19.5):
- RNReanimated (4.2.1):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Podfile.lock reanimated/worklets versions mismatch package.json and patch

Medium Severity

Podfile.lock resolves RNReanimated to 4.2.1 and RNWorklets to 0.7.1, but package.json declares react-native-reanimated at 4.3.1 and react-native-worklets at 0.8.3. The patch file is named react-native-reanimated+4.3.1.patch. These mismatches mean the stale lockfile would cause pod install to report version conflicts against the actual node_modules contents, and could lead to native/JS API mismatches if somehow resolved.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ccc63ae. Configure here.

The 3.19.4 → 3.19.5 bump dropped patches/react-native-reanimated+3.19.4.patch because the Synchronizable backport it contained is upstream in 3.19.5. That file also carried a ProMotion hunk in REANodesManager.mm — opt into preferredFrameRateRange(80, 120, 120) on iOS 15+ — which got removed by accident in the same delete. Without it, iOS clamps to 60 fps on non-ProMotion devices and never reaches 120 fps on ProMotion ones.
Flips on the new architecture on top of the RN 0.81 upgrade. Extracted
from the WIP attempt in #6924 (RN 0.81 + new arch combined).

Architecture flags:
- Android `gradle.properties`: `newArchEnabled=true`, adds
  `reactNativeReleaseLevel=experimental`.
- iOS `Podfile`: drops `:new_arch_enabled => false` so use_react_native!
  defaults to the new arch.
- iOS `*/Info.plist`: `RCTNewArchEnabled` flips to `true` and adds
  `ReactNativeReleaseLevel=experimental` on the main target.
- `MainApplication.kt`: sets
  `DefaultNewArchitectureEntryPoint.releaseLevel = EXPERIMENTAL`.

Library / patch swaps required by the new arch:
- `react-native-reanimated` 3.19.5 -> 4.2.1 (v3 is old-arch-only).
- Adds `react-native-worklets` (companion to reanimated 4); switches
  `react-native-reanimated/plugin` -> `react-native-worklets/plugin`
  in `babel.config.js`; Jest `transformIgnorePatterns` includes the
  package.
- Replaces `react-native-text-input-mask` (no new-arch support) with
  `react-native-advanced-input-mask` in ExchangeInput / GweiInputPill.
- `@react-native-masked-view/masked-view` 0.3.2 -> rainbow-me/masked-view
  fork to fix new-arch layout issues.
- Adds `patches/react-native-reanimated+4.2.1.patch`.
- `NavbarHeightModule`: refactors @ReactMethod sync getter into a
  separate `getNavigationBarHeightSync()`.

Native component migration to Fabric:
- Removes the old-arch `ios/Button.swift` / `ButtonManager.m`
  (RCTViewManager) and replaces with a codegen'd Fabric component at
  `src/codegen/specs/NativeButtonNativeComponent.ts` +
  `src/codegen/ios/Components/Button/ButtonComponentView.mm`. Wired up
  via a podspec + `react-native.config.js`. `ButtonPressAnimation.ios.tsx`
  consumes the codegen component directly.
- `react-native-cool-modals`: migrates Screen / ScreenStack / Portal from
  `RCTViewManager` + `RNCMScreen.m` to Fabric component views with
  ShadowNodes and ComponentDescriptors (`RNCMScreenComponentView.mm`,
  `RNCMScreenStackComponentView.mm`, `WindowPortalComponentView.mm`,
  `RNCMTouchHandler.mm`, `RNCMScreenViewController.mm`). Adds
  `react-native-cool-modals.podspec` and the `specs/` TS specs so the
  package participates in codegen.
- Updates `NativeStackView` / `Portal` / `createNativeStackNavigator` JS
  to consume the new specs.

Other cleanups carried over from #6924:
- Drops `src/components/AnimatedComponents/extendedPropsAllowlists.ts`
  and several `pickAnimatedProps` allowlists - Fabric no longer needs
  them.
- Removes the old `NativeButton.tsx` wrapper that called
  `requireNativeComponent('RNZoomableButton')`.
- iOS bridging header / pbxproj cleanup and `.clang-format` for the new
  Objective-C++ files.
The split into getNavigationBarHeight + getNavigationBarHeightSync was incidental drift from #6924's older base, not a new-arch requirement. develop intentionally consolidated this in #7317 (the sync method had a stray @ReactMethod exposing it twice).
…ch keys

OpenInRainbow / ShareWithRainbow had a 2-space → tab reformat that wasn't related to new arch. Rainbow/Info.plist had stale icon shuffling (added baggy/chonks/redacted, removed rnbw) that was already on develop differently. Reverted all three to #7366's base and re-applied only the new-arch keys: RCTNewArchEnabled=true plus ReactNativeReleaseLevel=experimental on the main target.
…Props.cpp (Fabric)

On the new arch we no longer hit the legacy ObjC RCTView path, so the kCACornerCurveContinuous hunk against RCTView.m never applied. Replace it with a Fabric-level fix that flips the default `BorderCurve` resolved value from Circular to Continuous in BaseViewProps so views without an explicit `borderCurve` prop still render with squircle corners.
The patch hunks are byte-identical to #7366's version. The only diff was the git blob hashes in the index lines and shifted line numbers in the @@ headers, both regenerated against a slightly different upstream snapshot. yarn install confirms #7366's patch applies cleanly.
Reanimated 4.3.1 is the current latest stable. Bumps the companion react-native-worklets to 0.8.3 (4.3.x peer-deps require 0.8.x). The existing patch hunks (REANodesManager ProMotion + colors.ts null check) apply unchanged, so only the patch filename moves.
Documents the two hunks (ProMotion frame range + colors.ts null guard). Confirmed neither is upstream on 4.3.1 stable or 4.4 nightly. Matches the RFC header convention used by the other patches in patches/.
@janicduplessis
janicduplessis force-pushed the @janic/enable-new-arch branch from ccc63ae to e21d014 Compare May 11, 2026 19:07

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e21d014. Configure here.

+ .borderCurves = borderCurves.resolve(isRTL, BorderCurve::Continuous),
.borderStyles = borderStyles.resolve(isRTL, BorderStyle::Solid),
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corner curve patch drops legacy interop view coverage

Medium Severity

The RCTView.m patch (legacy rendering path) was entirely replaced by the BaseViewProps.cpp patch (Fabric path), rather than keeping both hunks in the patch file. With new architecture enabled, third-party libraries that haven't migrated to Fabric still render through the legacy interop layer, where RCTView handles its own border rendering independently of BaseViewProps. Those views will lose continuous (squircle) corners and fall back to circular corners—a visual regression. The patch file format supports multiple file hunks, so both the legacy RCTView.m hunk and the new BaseViewProps.cpp hunk can coexist.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e21d014. Configure here.

@janicduplessis
janicduplessis force-pushed the @janic/rn-0.81-bump branch 2 times, most recently from 96b738b to a5986f9 Compare May 12, 2026 05:10
Base automatically changed from @janic/rn-0.81-bump to develop June 22, 2026 18:04
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