React Native 0.82.1 → 0.87.0 - #31
Merged
Merged
Conversation
The starter sat on 0.82.1 from October 2025; 0.87.0 shipped 11.08.2026, five minors later, and 54 of 60 packages were behind. What moves here moves because it cannot move alone. RN 0.87 requires React ^19.2.3; Reanimated 4.6.0 requires react-native 0.83-0.87 and worklets 0.12.x; the @react-native/* toolchain packages are version-locked to the runtime. Every one of those was verified against the registry rather than assumed. react-native-gesture-handler goes to 3.2.1 for the same reason, and only after the evidence said so: 2.29.1 does not compile against RN 0.87 at all — its Kotlin sources call getRootViewTag() and getZIndexMappedChildIndex(), which the new internals no longer expose. It was on the deliberately-excluded list until the build proved it belonged to the locked set. The app touches exactly one of its exports, GestureHandlerRootView, so the major carries no app-level risk. Navigation stays on 7.x by the repository's own rule (docs/TODO.md): v8 is still 8.0.0-alpha.43, so only minors within 7 are taken. react-native-mmkv and react-native-nitro-modules are untouched by the owner's decision — that pairing is where docs/development.md promises CxxPart failures, and the registry bears it out: mmkv 4.3.2 expects nitro 0.35.9 while npm's latest nitro is 0.37.0. Also dropped react-native-worklet (singular) — an empty 0.0.0 stub imported nowhere and confusable with the real react-native-worklets. The dozen unrelated majors now available — Babel 8, TypeScript 7, Jest 30, webview 14, bootsplash 7, i18next 26 and the rest — are deliberately not here. Babel 8 is outright incompatible (@react-native/babel-preset 0.87 depends on @babel/core ^7.25.2); the others are separate axes, and mixing them into an RN bump would make any breakage unattributable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three genuine removals, each confirmed against the installed runtime rather than guessed at. StyleSheet.absoluteFillObject is gone; absoluteFill is now the object itself, so the spread in HalfSheet reads the same and means the same. StatusBar lost backgroundColor and translucent. Android draws edge-to-edge always now, so the bar has no background of its own to colour — what shows behind it is whatever the screen paints there, and ScreenWrapper already paints it via the themed background on its SafeAreaView. The two screens that asked for a transparent bar under their gradient were asking for what is now the default, so their statusBarProps go away entirely rather than being rewritten. AppState.currentState is now typed as string, null or undefined: before the first event the state may genuinely be unknown, and 'unknown' is the value AppStateStatus already carries for that case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…it work The Android build was the whole cost of this upgrade, and none of it was guesswork — each step is what a failing build said, checked against the official 0.87 template. The root build.gradle no longer pins AGP. React Native ships its gradle plugin as a composite build, and that plugin carries its own AGP 9.2.1; a project that also declared its own version did not win the argument, it just produced nonsense — the applied plugin was 9.x while the declared one was 8.13.2, and the errors read as plugin is no longer required and does not specify compileSdk. The template form lets React Native own those versions, which is the only arrangement where they agree. android.builtInKotlin=false and android.newDsl=false are the two flags the 0.87 template sets, and they are load-bearing rather than tidy-up. AGP 9 otherwise forbids the org.jetbrains.kotlin.android plugin — which nine libraries in this dependency set still apply, screens, safe-area-context, gesture-handler, mmkv and nitro-modules among them, at their newest published versions — and demands the new DSL, which none of them are written in. Gradle goes to 9.4.1 because AGP 9 refuses to run on less, and buildTools and NDK move to the template's, since AGP 9 no longer promises to support the older ones. Kotlin stays at the project's 2.2.21, above the template's 2.2.0. proguard-android.txt is rejected outright by AGP 9 for carrying -dontoptimize. One workaround, and it is deliberately narrow. io.github.zhongwuzw 2.4.0 ships the com.tencent.mmkv namespace from two artifacts at once, which AGP 9's manifest merger will not accept; 2.4.1 separates them. Only that native artifact is forced. The npm packages react-native-mmkv and react-native-nitro-modules stay exactly where the owner left them. Verified: assembleDebug succeeds, the APK installs on a Pixel 8, and the app reaches Home with live data — the same thing maestro/smoke-android.yaml asserts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ut of reanimated React Native 0.87 no longer ships a jest preset at its own root — it is a separate package now, @react-native/jest-preset, versioned with the runtime. Without it jest refused to start at all: Module react-native should have jest-preset.js at the root. The reanimated babel plugin is the same shape of move. react-native-reanimated /plugin in 4.x is four lines re-exporting react-native-worklets/plugin, so pointing at the shim still worked; pointing at the real one is where it lives now, and shims do not last. All 41 tests pass on both changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pod install under the upgraded dependency set, with CocoaPods 1.17. The PrivacyInfo and pbxproj touches are the installer's own. The deployment target is left at 15.1: React Native 0.87 still reports min_ios_version_supported 15.1, so the assumption that it needed raising was wrong and nothing here changes it. Verified: xcodebuild for the simulator reports BUILD SUCCEEDED, the app installs on an iPhone 17 Pro simulator and reaches Home with live data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three places hard-coded 0.82.1 — the README badge, the README feature list and the stack line in .claude/CLAUDE.md — and all three now say 0.87.0. The rule forbidding version changes is rewritten rather than deleted. What it was protecting is real: an agent that bumps a dependency to dodge a bug leaves a codebase nobody can reason about. What it could not express is that an upgrade is sometimes the work itself. It now forbids the initiative, not the act, and says why version changes must not ride along inside an unrelated diff — a break that follows a mixed change cannot be attributed to anything. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Стартер стоял на 0.82.1 от октября 2025. Актуальная — 0.87.0 от 11.08.2026, пять минорных релизов спустя; 54 пакета из 60 отставали.
Проверено на обеих платформах: собирается, ставится и доходит до экрана
Homeс живыми данными — Android на Pixel 8, iOS в симуляторе iPhone 17 Pro.Как это делалось
Сначала снят базовый прогон до обновления —
tsc0 ошибок, lint чист, 41 тест,assembleDebugуспешен. Без него нельзя было бы отличить свои поломки от уже существовавших. Каждое утверждение о совместимости проверялось в реестре npm или падающей сборкой, а не по памяти.Что двигалось и почему
Связанный набор — то, что не умеет двигаться поодиночке: RN требует
react ^19.2.3; Reanimated 4.6.0 требуетreact-native 0.83–0.87иworklets 0.12.x; пакеты@react-native/*привязаны к версии рантайма.react-native-gesture-handler2.29.1 → 3.2.1 попал сюда по факту, а не по плану: он вообще не компилируется с RN 0.87 — его Kotlin-исходники зовутgetRootViewTag()иgetZIndexMappedChildIndex(), которых во внутренностях больше нет. Он был в списке исключённых мажоров, пока сборка не доказала обратное. Приложение использует ровно один его экспорт,GestureHandlerRootView.Навигация остаётся на 7.x по правилу самого репозитория (
docs/TODO.md): v8 всё ещё8.0.0-alpha.43.react-native-mmkvиreact-native-nitro-modulesне тронуты решением владельца — это связка, гдеdocs/development.mdобещает ошибкиCxxPart, и реестр это подтверждает: mmkv 4.3.2 ждёт nitro 0.35.9, а последний в npm — 0.37.0.Дюжина мажоров сознательно не взята — Babel 8, TypeScript 7, Jest 30, webview 14, bootsplash 7, i18next 26 и прочие. Babel 8 просто несовместим (
@react-native/babel-preset@0.87зависит от@babel/core ^7.25.2); остальные — отдельные оси, и смешать их с обновлением RN значило бы сделать любую поломку неатрибутируемой.Что сломалось в прикладном коде
Три настоящих удаления API, каждое сверено с установленным рантаймом:
StyleSheet.absoluteFillObjectбольше нет — теперь самabsoluteFillи есть объект;StatusBarлишилсяbackgroundColorиtranslucent: Android рисует edge-to-edge всегда, у полосы нет своего фона. Два экрана просили прозрачную полосу под градиент — это стало умолчанием, так что ихstatusBarPropsушли целиком;AppState.currentStateтеперь может бытьnull/undefined— до первого события состояние честно неизвестно.Android — вся цена обновления
RN отдаёт свой gradle-плагин composite-сборкой, и тот несёт AGP 9.2.1. Проект, который объявлял собственную версию, спор не выигрывал — применялся 9.x, а объявлен был 8.13.2, и это вылезало как «plugin is no longer required» и «does not specify compileSdk». Корневой
build.gradleприведён к форме шаблона 0.87, где версиями владеет сам RN.Два флага из шаблона оказались несущими, а не косметикой:
Gradle → 9.4.1 (AGP 9 на меньшем не работает), build-tools и NDK — к шаблонным,
proguard-android.txt→-optimize(первый AGP 9 отвергает за-dontoptimize).Один обходной путь, намеренно узкий:
io.github.zhongwuzw2.4.0 отдаёт пространство имёнcom.tencent.mmkvсразу из двух артефактов, слияние манифестов AGP 9 это не принимает; в 2.4.1 разведено. Поднят только нативный артефакт — npm-пакеты mmkv и nitro остались нетронутыми.Инструменты
Пресет Jest уехал из корня
react-nativeв отдельный пакет@react-native/jest-preset— без него jest не стартовал вовсе. Babel-плагин Reanimated в 4.x — четыре строки реэкспорта изreact-native-worklets/plugin; путь исправлен на настоящий.Проверка
tsc --noEmitbiome checkApp.test.tsx— он поднимает всё деревоcheck:icons/check:importsassembleDebug✅, установлено на Pixel 8, дошло доHomeBUILD SUCCEEDED✅, симулятор iPhone 17 Pro, дошло доHomeМинимум iOS остаётся 15.1 — RN 0.87 по-прежнему требует именно его; предположение, что нужно поднимать, оказалось неверным.
Осталось за рамками
docs/OPERATIONS.mdв README, AGENTS.md и CLAUDE.md — существовали до этой ветки, не трогал, чтобы не размывать диф.🤖 Generated with Claude Code