Version matrix
Checked on 2026-10-10. Update this file whenever a native dependency is added or upgraded.
Toolchain
Section titled “Toolchain”| Tool | Version | Source |
|---|---|---|
| React Native | 0.87.1 | react-native-community/template 0.87-stable |
| React | 19.2.3 (exact, must match RN’s renderer) | template |
| @react-native-community/cli | 20.2.0 | template |
| Node | ≥ 22.11 (.nvmrc: 24) |
template engines |
| pnpm | 12.3.4 (packageManager), policy needs ≥ 11 |
ADR 0003 |
| TypeScript | 6.0 | template |
| Gradle / AGP / Kotlin | 9.4.1 / 9 / 2.2.0 | template |
| Android SDK | compile 37, target 36, min 24; build-tools 37.0.0 | template |
| Android NDK | 27.1.12297006 | template (see note on Unistyles below) |
| Java | 21 | Gradle 9 requirement |
| Xcode | 27.0 | local. CI’s macos-26 image has Xcode 26.x (26.6 default, September 2026); whether the project builds with it is unchecked until CI runs. The iOS 27 SDK requires the UIScene life cycle, which the RN 0.87 template lacks; see docs/native-changes.md#uiscene |
| iOS deployment target | 15.1 (RN 0.87 min_ios_version_supported) |
template |
| CocoaPods | 1.15.2 via Bundler | Gemfile |
| Ruby | 3.2.2 (.ruby-version) |
Gemfile.lock; fastlane warns it will need 3.3 |
| fastlane | 2.240.1 via Bundler | Gemfile (2.241.0 was 2 days old); needs json < 3, so json is 2.21 |
Libraries chosen in Phase 0
Section titled “Libraries chosen in Phase 0”“Android build” means all 32 libraries below (everything except the opt-in SQLite module) were compiled
together into one Android debug APK (arm64) on the toolchain above: BUILD SUCCESSFUL, 962 tasks, 2026-10-10.
iOS: the same 32 libraries compiled together for the simulator (1,845 compile units) with
$RNFirebaseDisableSPM = true and static frameworks; the only failing step was Crashlytics’ build phase, which
needs a real GoogleService-Info.plist (made conditional in Phase 3). See docs/native-changes.md.
“Peers” means the published peer ranges allow RN 0.87 / React 19.2.
| Library | Version | Role (ADR) | Peers | Android build | Notes |
|---|---|---|---|---|---|
| @react-navigation/native, native-stack, bottom-tabs | 7.5 / 7.20 / 7.20 | Navigation (0007) | ✓ | ✓ | v8 still alpha |
| react-native-screens | 4.28 | Navigation | ✓ | ✓ | 4.29 held back by the 3-day release age |
| react-native-safe-area-context | 5.10 | Layout | ✓ | ✓ | |
| react-native-gesture-handler | 3.3 | Gestures (0008) | ✓ | ✓ | Major 3: new API |
| react-native-reanimated / worklets | 4.7.1 / 0.13.0 | Animation (0008) | RN 0.86–0.88 | ✓ | |
| react-native-unistyles | 3.5.1 | Styling (0008) | ✓ | ✓ | Doesn’t read ndkVersion; Phase 2 forces the app NDK on all subprojects |
| react-native-nitro-modules | 0.37.1 | Unistyles, MMKV | ✓ | ✓ | |
| react-native-edge-to-edge | 1.8.2 | Unistyles peer | ✓ | ✓ | |
| react-native-mmkv | 4.3.2 | Storage (0006) | ✓ | ✓ | Nitro-based |
| react-native-keychain | 10.0.0 | SecureStore (0010) | ✓ | ✓ | Last release 2025-03; behind a port |
| @shopify/flash-list | 2.3.3 | Lists (0008) | ✓ | ✓ | |
| @gorhom/bottom-sheet | 5.2.14 | Sheets (0008) | ✓ | ✓ | |
| lucide-react-native + react-native-svg | 1.52 / 15.15.5 | Icons (0008) | ✓ | ✓ | |
| @d11/react-native-fast-image | 8.13.0 | Image port (0008) | ✓ | ✓ | |
| @shopify/react-native-skia | 2.14.0 | Graphics (0008) | ✓ | ✓ | Largest native dependency |
| lottie-react-native | 7.5.0 | Animation (0008) | ✓ | ✓ | |
| react-native-localize | 3.7.2 | i18n (0009) | ✓ | ✓ | |
| react-native-config | 1.7.2 | Env (0004) | ✓ | ✓ | |
| react-native-bootsplash | 7.3.4 | Splash | ✓ | ✓ | Pulls @expo/config-plugins (trust exception in pnpm policy) |
| @react-native-firebase/app, messaging, crashlytics, analytics, remote-config | 26.4.0 | Observability, push (0010) | ✓ | ✓ | iOS needs static frameworks; plugins only when configured. Firebase iOS SDK 12.18 via CocoaPods: Firebase publishes no new CocoaPods versions after October 2026; recheck at every RNFB upgrade |
| react-native-notify-kit | 10.8.0 | Push display (0010) | ✓ | ✓ | Maintained Notifee fork (Notifee archived 2026-04) |
| @react-native-community/netinfo | 12.0.1 | Offline (0006) | ✓ | ✓ | |
| react-native-permissions | 5.6.2 | Permissions | ✓ | ✓ | |
| react-native-device-info | 15.0.2 | Force update | ✓ | ✓ | |
| react-native-keyboard-controller | 1.22.6 | Forms (0009) | ✓ | ✓ | |
| @op-engineering/op-sqlite + drizzle-orm | 18.2.5 / 0.45 | Offline DB module (0006, opt-in) | ✓ | not yet | Checked when the module is built |
Dev tooling notes
Section titled “Dev tooling notes”-
MSW 2.15 (not 3). MSW 3 removed
msw/native; its docs point to@msw/react-native, which isn’t published on npm yet (404 on 2026-10-10). Move to it when it ships. Jest mapsmsw/nodeexplicitly because the RN preset resolves thereact-nativeexport condition, under whichmsw/nodeis null. In the app, mocks run through an axios adapter calling MSW’sgetResponse()(src/mocks/native.ts), notmsw/native’ssetupServer: its XHR interceptor needs browser globals Hermes lacks and broke on RN’s XHR.src/mocks/polyfills.tsstill adds the few globals MSW touches (Event,EventTarget,MessageEvent,BroadcastChannel, streams viaweb-streams-polyfill, URL viareact-native-url-polyfill). -
Babel:
@babel/plugin-transform-export-namespace-from(Zod usesexport * as) and@babel/plugin-transform-class-static-block(MSW shipsstatic {}blocks Hermes doesn’t parse). -
intl-pluralrules 2.0.1, relative-time-format 1.1.12: Hermes (RN 0.87) has no
Intl.PluralRulesorIntl.RelativeTimeFormat. Without them i18next plurals fail silently andformatRelativethrows “undefined cannot be used as a constructor”. Replaced the FormatJS polyfills, whose locale matcher also needsIntl.Locale(~390 KB in total against ~50 KB). Checked in Hermes for English and Arabic. -
zod 4: its
localesbarrel re-exports ~60 languages Metro can’t tree-shake.metro.config.jsresolves it tometro/zod-locales.js(English only, −295 KB). The app’s validation messages are its own translation keys, so nothing user-visible changes. -
lucide-react-native: import icons only through
src/shared/ui/icons.ts(one module per icon). A root import bundles all ~1,500 icons: the release JS bundle went from 5.74 MB to 3.86 MB after switching. ESLint enforces it. -
React Native Testing Library 14:
render,fireEventanduserEventare all async; alwaysawaitthem. -
Gesture Handler 3 in Jest:
src/test/pre-setup.tsstubs worklets’getUIRuntimeHolder, which the Jest build of react-native-worklets 0.13 throws on when Gesture Handler installs its UI-runtime bindings. -
ESLint 9.39 (maintenance line). ESLint 10 is out, but
@react-native/eslint-config,eslint-plugin-reactandeslint-plugin-react-nativedon’t declare support yet.eslint-plugin-ft-flowis overridden to 3.x for ESLint 9. -
React Native Testing Library 14 uses
test-renderer(notreact-test-renderer).test-rendereris pinned to~1.2.0because 1.3 needs React 19.3.