Target the latest stable RN (0.87, npm 0.87.1); the New Architecture is non-optional from 0.82, so plan any migration around that line. Budget the 0.87 step as type-codemod work (the Strict TypeScript API default) and finish it during the 0.88 cycle, because the legacy-deep-imports bridge is removed in the release after 0.88.
on RN < 0.82 → New-Arch migration first (Legacy frozen at 0.80 with no fixes, stripped at 0.84)
on RN ≥ 0.86 → remove react-native-edge-to-edge (edge-to-edge is in core)
moving to 0.87 → Strict TypeScript API migration (fix deep imports into react-native/Libraries/* with the /migrate-to-strict-api skill + the ESLint fixers; the react-native-legacy-deep-imports customCondition is a bridge that exists only through 0.88)
still importing InteractionManager, Modal `animated` or StatusBar backgroundColor/translucent → requestIdleCallback and the current props (0.87 removed them; migrate now)
still importing core SafeAreaView → react-native-safe-area-context (deprecated since 0.81; not in 0.87's removal list)
on iOS and tempted by SwiftPM → CocoaPods (still the default and supported path; SwiftPM is experimental) — and audit which iOS deps publish only through trunk, which goes read-only 2026-12-02
on Expo → SDK 57 (RN 0.86) for production; SDK 58 beta (RN 0.88 RC) only if you need iOS 27 support now
want React 19.3 on native → RN 0.88 (RC; the first line to sync it — RB-E-REACT-CORE)
quoting a pre-0.84 row as authoritative → that version's RN blog (verify the row first)
Options & tradeoffs
the field considered — and why each one isn’t the default here
option
tradeoff
RN 0.76 (2024-10)
New Arch ON by default; React Native DevTools; boxShadow + filter; iOS 15.1 / Android API 24 min; `react-native init` deprecated → Expo recommended
CURRENT STABLE. Strict TypeScript API becomes the default (deep imports into react-native/Libraries/* are type errors); Metro 0.87 (source maps 2x faster); experimental SwiftPM; AGP 9; floor Node ≥22.13 / Kotlin 2.0+ / compileSdk 37; InteractionManager removed. 0.87.1 (2026-08-26, verified vs npm 2026-09-24); see note: 0.87 IS THE STRICT-API LINE.
RN 0.88 (RELEASE CANDIDATE, not shipped)
0.88.0-rc.3 (2026-09-29, verified vs the GitHub release; npm tag `next`): plan for it, do not target it. States it is non-breaking, syncs React 19.3.0, and is the LAST line with the react-native-legacy-deep-imports escape hatch; Expo SDK 58 beta ships it. See note: 0.88 RC.
Migration lines
version and deprecation lines this decision tracks — for YOUR repo's sequenced plan: the doctor or npx -y @heart-it/react-brain migrate .
the ladder crosses hard lines: New Architecture is the ONLY runtime ≥0.82 (Legacy frozen at 0.80, stripped at 0.84), Hermes V1 + precompiled iOS binaries default at 0.84 (Node 22.11+ min), the Jest preset moves to @react-native/jest-preset at 0.85, Android edge-to-edge lands in core at 0.86, and 0.87 makes the Strict TypeScript API the default (deep imports into react-native/Libraries/* become type errors) while raising the floor to Node 22.13 / Kotlin 2.0 / compileSdk 37 and dropping InteractionManager. Every New-Arch-gated upgrade below unblocks at 0.82+; budget the 0.87 step as type-codemod work.
A React Native version number is an architecture timeline: where you stand decides what code you may write. The New Architecture went default (0.76) → Legacy frozen (0.80) → mandatory (0.82) → Legacy stripped (0.84), so 0.82 is the line every other New-Arch-gated upgrade waits on. Above it, staying current is cheap (bi-monthly cadence, two zero-breaking-change releases) except for one rung: 0.87 turned the Strict TypeScript API on, and 0.88 is the last line that lets you opt out. Rows marked ✓ (0.84–0.87) are verified vs the official RN blogs; 0.76–0.83 come from release history + RN Rewind, so verify a row against its RN blog before quoting it.
0.87 IS THE STRICT-API LINE (2026-08-11, verified vs the release blog + the v0.87.0 CHANGELOG): the 0.80 opt-in preview became the default, so a 0.87 bump is a TYPE migration first and a runtime migration second → plan it as codemod work (the /migrate-to-strict-api skill and the ESLint fixers) and treat the "react-native-legacy-deep-imports" tsconfig customCondition as a bridge: the blog says it remains available through 0.88 and the legacy types go in the following release. Also in 0.87: Metro 0.84→0.87 (source maps 2x faster, half the source-map memory, stable TS/ESM config files, .es6 + YAML configs dropped); EXPERIMENTAL Swift Package Manager for iOS (`npx react-native spm`; CocoaPods stays the default and the supported path); AGP 9 support (opt out of built-in Kotlin and the new DSL via android.builtInKotlin=false / android.newDsl=false); toolchain floor Node ≥22.13, Kotlin 2.0+, minCompileSdk 34, compileSdk/buildTools 37, so CI images move with it. Removals: InteractionManager (→ requestIdleCallback), Modal `animated`, StatusBar backgroundColor/translucent/networkActivityIndicatorVisible, boolean keyboardShouldPersistTaps, the useTurboModules flag, the Touchable root export, NativeMethods types, *Properties type aliases. Deprecated (not removed): ImageBackground (→ a View with an absolutely positioned Image). backgroundImage loses its experimental_ prefix. 0.84.x moves to unsupported. Hermes: 0.87 pins hermes-compiler 250829098.0.16 (verified vs the tag's package.json), past the .15 backport that fixes the worklet debug-metadata memory regression; RB-E-ANIMATION owns that thread.
0.88 RC (rc.1 2026-09-16, rc.2 2026-09-22, rc.3 2026-09-29, verified vs the GitHub releases): SYNCS REACT 19.3.0 (npm peer react ^19.3.0; the first RN line to carry it), bumps Hermes to 260318099.0.3, enables the Web-based event-dispatching refactor and, in canary, the imperative EventTarget API (addEventListener/removeEventListener/dispatchEvent) on native view refs. SwiftPM manifests derive their iOS floor from the app's IPHONEOS_DEPLOYMENT_TARGET instead of hardcoding iOS 15, so higher-minimum deps (Expo at 16.4) autolink. rc.2 is a stabilisation cut: minimum Metro 0.87.1; it reverts two TurboModule ArrayBuffer codegen changes that broke modules adopting ObjC/C++ ArrayBuffer support in 0.87 (the notes state "0.88 is a non-breaking release"); and it restores the .js fallback for legacy deep imports under the react-native-legacy-deep-imports condition → the deadline that matters is still the removal of that condition after 0.88. rc.3 is a further stabilisation cut: Hermes to 260318099.0.4 (fixes a quadratic reserve() during lazy compilation), a Metro package-exports warning fix, and a SwiftPM autolinking fix that stopped recreating library package roots on every sync (was breaking Xcode builds for libraries shipping their own Package.swift).
EXPO SDK 58 BETA IS THE 0.88 ON-RAMP (2026-09-15, verified vs the Expo changelog): it upgrades RN from 0.86 straight to the 0.88 RC, so no stable Expo SDK carries 0.87. The beta runs three to four weeks; Expo will move to 0.88 stable when it ships and release SDK 58 shortly after, so the two dates are coupled, and an app that needs iOS 27 support now can ship on the beta. (1) iOS 27's SDK REQUIRES THE UISCENE LIFE CYCLE: `npx expo prebuild` generates SceneDelegate.swift plus a UIApplicationSceneManifest entry, and the UIWindow is created by the scene delegate rather than in didFinishLaunchingWithOptions; Expo forwards URL, user-activity, Handoff, life-cycle and quick-action events to ExpoAppDelegate so config plugins keep working → if you manage the ios directory BY HAND, this migration is yours. (2) The 0.87 removals above arrive with SDK 58. (3) SwiftPM advanced without becoming the default: Expo's iOS sources now compile as separate SwiftPM targets and an Expo autolinking plugin for `npx react-native spm` is in review → the "experimental" judgment holds.
EXPO GO NOW REQUIRES LOGIN (SDK 57, verified vs expo.dev/changelog/expo-go-57-login): the same account must be signed in on BOTH the terminal (`npx expo login`) and the Expo Go app, or scanning the QR code fails with a message naming the missing side. It applies to the latest iOS Expo Go only (Android stated as later), NOT to simulator builds and NOT to development builds → one more reason a team past prototyping should be on a dev build.
EXPO SDK MAPPING: SDK 55 = RN 0.83 + React 19.2; SDK 56 = RN 0.85 + Expo UI stable; SDK 57 = RN 0.86 + React 19.2 (2026-06, verified vs the SDK 57 changelog; skipped the beta phase because 0.86 was breaking-change-free); SDK 58 beta = RN 0.88 RC + React 19.3 (verified vs expo@58.0.0-preview.6 bundledNativeModules, 2026-09-24).
APPLE PLATFORM FLOOR (2026-07-10, verified vs the Expo post's body): since 2026-04-28, App Store Connect uploads MUST be built with Xcode 26 / iOS-26-family SDKs. Expo SDK 54/55 EAS images already run Xcode 26; SDK ≤53 needs an explicit image opt-in → stay ≥SDK 54.
COCOAPODS TRUNK GOES READ-ONLY 2026-12-02 (verified vs the CocoaPods blog; newsletter framing of this is overstated): CocoaPods has been in maintenance mode since 2024-08-13; on 2026-12-02 trunk permanently refuses NEW podspecs and the Specs repo is archived (test run 2026-11-01→07). The Specs repo and CDN keep serving, so existing builds and pod versions keep working and private specs repos are unaffected; what stops is any library publishing a NEW version through trunk. So SwiftPM is the ecosystem's answer to a dated deadline, but still experimental → today's move is to know which iOS dependencies still resolve only through trunk.
RB-E-REACT-CORE owns what each React version adds; RB-E-TYPESCRIPT owns TypeScript itself; RB-E-ANIMATION owns the Hermes worklet-memory regression; the react-native-jsi skill owns New-Arch internals.
HISTORY: 2026-10-01 REOPENED: 0.88 row moved to rc.3 (Hermes 260318099.0.4 quadratic-reserve fix, Metro package-exports warning fix, SwiftPM autolinking fix). 2026-09-24 CORRECTED: core SafeAreaView is deprecated, not removed, in 0.87 (the when-clause said removed); 0.88 row moved to rc.2. 2026-09-21 GAP CLOSED: RN 0.88 RC row (React 19.3 sync, Hermes bump, deep-imports bridge ends after 0.88) + Expo SDK 58 beta / iOS 27 UIScene. 2026-09-10 GAP CLOSED: Expo Go (SDK 57, iOS) requires login on CLI and app. 2026-08-18 GAP CLOSED: RN 0.87 stable — Strict TS API default, Metro 0.87, experimental SwiftPM, AGP 9. 2026-07-28 GAP CLOSED: 0.87 RC.3 — SwiftPM chain backported + Hermes worklet-metadata fix. 2026-07-10 GAP CLOSED: Apple Xcode 26 upload floor.
Canonical reading
Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.
Traces RN's architecture from the old async JSON bridge to the New Architecture (JSI, Fabric's immutable C++ shadow tree, Turbo Modules, Codegen), explaining why each piece exists. The 'why it changed' narrative behind the 0.76→0.82 version arc — not a release note.
Depth (in-domain rules) is owned by the react-native-jsi skill — this entry is selection breadth.
The full explanation
The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.
About the React Native release timeline — the version ladder
Diataxis: Explanation. This page builds understanding of what an RN version number
means — why the release timeline reads as an architecture timeline, not a changelog. It is
not an upgrade how-to; the sequenced upgrade knowledge lives in the index entry's migrate:
block (assembled by react-brain migrate), and the per-version rows themselves live in the
index entry RB-E-RN-VERSIONS. New-Architecture internals — JSI, Fabric, TurboModules,
Codegen — are owned by the react-native-jsi skill. Read this for the why.
The one idea that organises everything: the version number is a rung, not a date
A React Native version tells you more than when a project last ran npm install — it tells
you what code that project is allowed to write. The entry's own migrate guidance says it
plainly: "the ladder crosses hard lines." Climb the rows and three kinds of permission change
under your feet:
Architecture permission. New Architecture ON by default (0.76) → Legacy Architecture
FROZEN (0.80, dated 2025-06-02) → New Architecture MANDATORY — opt-out ignored
(0.82) → Legacy-Arch stripped (0.84). Below 0.82 legacy code still runs, but on an
architecture receiving no fixes; at 0.82 the opt-out stops working; at 0.84 the code you
would fall back to is no longer shipped.
Engine permission. JavaScriptCore removed — Hermes-only (0.81) → Hermes V1
experimental (0.82) → Hermes V1 DEFAULT plus precompiled iOS binaries by default
(0.84). The engine stopped being a choice at 0.81; from 0.84 its next generation is simply
what you get.
API permission.react-native init deprecated → Expo recommended (0.76); forwardRef
deprecated → ref-as-prop (0.78, with React 19); core SafeAreaView deprecated →
safe-area-context (0.81); and 0.87 removes long-deprecated APIs (InteractionManager,
Modal animated, the deprecated StatusBar props), turns deep imports into
react-native/Libraries/* into type errors under the now-default Strict TypeScript API,
and deprecates ImageBackground. Core SafeAreaView is still deprecated, not removed.
Deprecations on one rung become removals a few rungs up.
The floors move too, on other people's schedules: iOS 15.1 / Android API 24 minimums (0.76),
Android 15's 16KB pages (0.77), Android 16 / API 36 with mandatory edge-to-edge (0.81), Node
22.11+ (0.84) — and, outside RN's ladder entirely, Apple's rule that since 2026-04-28 App
Store Connect uploads must be built with Xcode 26 / iOS-26-family SDKs.
The default, and why
Target the latest stable RN (0.87, npm 0.87.1); the New Architecture is non-optional from
0.82, so plan any migration around that line. Budget the 0.87 step as type-codemod work (the
Strict TypeScript API default) and finish it during the 0.88 cycle, because the
legacy-deep-imports bridge is removed in the release after 0.88.
The recommendation names one line, because one line dominates: 0.82 is where the New
Architecture became the only runtime. Everything below it sits on an architecture frozen at
0.80 (no fixes) and stripped at 0.84 — the entry's when-clause is blunt: on RN < 0.82, budget
a New-Arch migration. And per the migrate block, every New-Arch-gated upgrade elsewhere in a
stack "unblocks at 0.82+" — the line is a dependency of other decisions, not just this one.
Meanwhile the top of the ladder got cheaper to stand on: RN runs a bi-monthly cadence (0.84),
0.86 was the second zero-breaking-change release, and Expo shipped SDK 57 against it having
skipped the beta phase precisely because 0.86 was breaking-change-free — with Expo testing
near-immediate optional upgrades as RN moves to six releases a year. Staying current is
routine; falling behind the architecture line is the expensive state.
What changed the plan since 0.86 (verified 2026-09-24)
0.87 (stable 2026-08-11) is a type migration. The Strict TypeScript API became the default,
so deep imports into react-native/Libraries/* are type errors. The
react-native-legacy-deep-imports customCondition is a bridge that the release blog keeps only
through 0.88; the legacy types go in the release after. 0.87 also raises the toolchain floor
(Node ≥ 22.13, Kotlin 2.0+, compileSdk 37). 0.88 is a release candidate (rc.2, 2026-09-22):
it states it is non-breaking, syncs React 19.3.0, and restores the .js fallback for legacy
deep imports under that same condition, so the deadline is unchanged. Expo SDK 58 beta jumps
from RN 0.86 straight to the 0.88 RC (no stable SDK carries 0.87) and requires the UIScene life
cycle for iOS 27, which is your migration if you maintain the ios directory by hand.
CocoaPods trunk goes read-only on 2026-12-02: existing pods keep resolving, but no library can
publish a new version through trunk, which is why 0.87's experimental SwiftPM path matters even
though CocoaPods stays the default.
The landscape: reading the rungs
The entry's thirteen rows (0.76 → 0.88 RC) are one row per version, each carrying that
version's durable change. Beyond the three permission lines above, the rows carry a steady
capability drip: CSS-ish styling — display:contents, box-sizing, mixBlendMode,
outline (0.77) after boxShadow + filter (0.76); React 19 with Actions,
useActionState, useOptimistic, use, and the React Compiler (0.78), the Compiler
reaching the default template as RC (0.81), React 19.2 with <Activity> and
useEffectEvent (0.83); DOM Node APIs (0.82) and Intersection Observer (0.83); Metro ~3x
faster cold start via deferred hashing, with remote-JS Chrome debugging removed (0.79); React
Native DevTools (0.76), its network inspector (0.83), light/dark (0.86); opt-in Strict
TypeScript API with types from source (0.80); debugOptimized Android variant (0.82);
prebuilt artefacts (0.83), precompiled iOS opt-in (0.81) then default (0.84); a shared C++
animation backend and the Jest preset moving to @react-native/jest-preset (0.85), the
layout-prop native driver landing in 0.85.1; Android-15+ edge-to-edge in core (0.86).
Two usage notes carry the entry's trust model:
Verification is per-row. Rows marked ✓ (0.84–0.87) are verified against the official RN
release blogs; 0.76–0.83 come from release history + RN Rewind — verify a specific row
against the RN blog before quoting it as authoritative. The 0.88 row is verified against the
rc.2 GitHub release and must be re-verified against the release blog when stable lands.
Most teams ride the ladder via Expo. The entry's mapping: SDK 55 = RN 0.83 + React
19.2; SDK 56 = RN 0.85 + Expo UI stable; SDK 57 = RN 0.86 + React 19.2 (2026-06, verified
against the SDK 57 changelog); SDK 58 beta = RN 0.88 RC + React 19.3. The Apple floor lands here too: SDK 54/55 EAS images already
run Xcode 26, SDK ≤ 53 needs an explicit image opt-in — practically, stay ≥ SDK 54.
For the why it changed behind the 0.76→0.82 arc — the old async JSON bridge giving way to
JSI, Fabric's immutable C++ shadow tree, Turbo Modules, Codegen — the entry's reading is
Felipe Ramalho's "React Native Architecture: From Bridge to Fabric" (Codeminer42): a
narrative, not a release note.
Tradeoffs and failure modes to name out loud
Camping below 0.82. Legacy has been frozen since 0.80 and stripped since 0.84; the
when-clause is to budget a New-Arch migration, and the migrate block prices it at effort L
with urgency upgrade. This is the rung where waiting compounds.
Carrying polyfills past the rung that absorbed them.react-native-edge-to-edge is
redundant on ≥ 0.86 — Android-15+ edge-to-edge is in core; the entry's second migrate row
exists to delete the package.
Importing on borrowed time. Still importing InteractionManager or deep paths → migrate
now; 0.87 removed the first and made the second a type error. Core SafeAreaView is
deprecated (since 0.81) but not removed; move to react-native-safe-area-context anyway.
Quoting pre-0.84 rows as gospel. The corpus's own discipline: history-sourced rows get
verified against the RN blog before load-bearing use.
Missing floors that aren't RN's. Node 22.11+ arrived with 0.84; the Xcode 26 upload
rule binds since 2026-04-28 regardless of your RN version. Floors don't negotiate.
Assuming an upgrade only touches app code. 0.85 moved the Jest preset to
@react-native/jest-preset — some rungs break config, not components.
How it interacts with the rest of the stack
New Architecture depth (react-native-jsi, RB-E-NATIVE). The ladder is the
schedule of the New Architecture; what JSI, Fabric, TurboModules, and Codegen actually
are belongs to the skill and the native entry.
Build & engine (RB-E-BUILD). Hermes V1, precompiled iOS binaries, prebuilt artefacts,
and Metro's cold-start work are the build-time face of the same rungs.
React itself (RB-E-REACT-CORE). RN versions pin React versions — 0.78 → React 19,
0.83 → React 19.2, 0.88 → React 19.3. The Actions/Compiler era arrives via this ladder.
Testing (RB-E-TESTING). The 0.85 Jest-preset move is where the ladder reaches into
test config.
The migrate tool. The entry's migrate: block is this page made executable: installed
react-native below 0.87 → a sequenced, receipted upgrade case assembled by
react-brain migrate.
In one paragraph
The React Native version number is an architecture timeline wearing a release number:
where you stand on the ladder decides what code you're allowed to write. The New Architecture
went default (0.76) → Legacy frozen (0.80) → mandatory, opt-out ignored (0.82) → Legacy
stripped (0.84); the engine went Hermes-only (0.81) → Hermes V1 default with precompiled iOS
binaries (0.84); deprecated APIs (InteractionManager, deep imports) fall off at 0.87,
which also makes the Strict TypeScript API the default. Target the latest stable (0.87), budget
that step as type-codemod work before the deep-imports bridge ends after 0.88, plan any
migration around the 0.82 line, verify pre-0.84 rows against the RN blog before quoting
them, and respect the floors that move on Apple's and Node's schedules, not yours.
See also: RB-E-NATIVE (what the New Architecture is), RB-E-BUILD (Hermes, precompiled
binaries, Metro), RB-E-REACT-CORE (the React versions each RN pins), RB-E-TESTING (the
0.85 Jest-preset move). New-Arch internals — JSI, Fabric, TurboModules, Codegen: the
react-native-jsi skill. Background reading: Felipe Ramalho, "React Native Architecture:
From Bridge to Fabric" (Codeminer42).