React Native Roadmap 2026-W18

Week of April 27–May 3, 2026

Items This Week

#TitleLabelLink
1EAS Gradle cache for Android builds🟧 EXPORead
2The Real Cost of React Native Animations: Benchmarking Every Approach🟦 RNRead
3Part 1: How to Make Pure JSI Code Faster in React Native🟦 RNRead
4RFC: Swift Package Manager support — CocoaPods deprecation🟦 RNRead
5React Compiler 18-month retrospective & Rust rewrite tested at Meta⚛️ REACTRead

5-Day Action Plan

 


🟧 Chunk 1 — Enable EAS Gradle Cache for Android Builds

Goal: Reduce Android EAS build times by ~50% by enabling Gradle task caching, freeing developer time and tightening CI/CD feedback loops.

Scope:

  • Add EAS_GRADLE_CACHE: "1" to the relevant build profiles in eas.json
  • Verify cache is active by inspecting the "Run Gradle" step in EAS build logs for FROM CACHE markers
  • Document the change in the project README or CI setup guide

Out of scope: Compiler cache (ccache) setup, iOS build optimizations, local Gradle caching outside EAS.

Dependencies: Active EAS account with at least one Android build profile configured in eas.json.

Acceptance criteria:

  • EAS Android build logs show tasks marked FROM CACHE on the second run
  • Build time on a repeat build is at least 30% faster than a clean build
  • eas.json change is committed and reviewed

Estimated effort: XS

 

Copy/paste this prompt:

Implement the following React Native chunk for your mobile app: Configure EAS Gradle Cache for Android to speed up builds by ~50%.

Goal: Enable Gradle task caching on EAS Build so subsequent Android builds reuse cached outputs instead of recomputing them from scratch.

Files to modify:

  • eas.json — add EAS_GRADLE_CACHE: "1" env var to your build profiles (production, preview, development)

Step-by-step:

  1. Open eas.json and locate the build section.
  2. For each Android profile (e.g., production, preview), add:
"env": {
  "EAS_GRADLE_CACHE": "1"
}
  1. Commit and push the change.
  2. Trigger an EAS build: eas build --platform android --profile production
  3. After the first build completes, trigger a second build immediately (no code changes).
  4. Open the second build logs → "Run Gradle" step → verify tasks show FROM CACHE.

Acceptance criteria checklist:

  • eas.json updated with EAS_GRADLE_CACHE: "1" in all relevant profiles
  • Second EAS build shows FROM CACHE in the Gradle step
  • Total build time on repeat run is measurably shorter (≥30%)
  • No regression in build output (APK/AAB works as expected)

🟦 Chunk 2 — Migrate Ambient Animations to react-native-ease

Goal: Eliminate per-frame UI thread overhead on long-running animations (skeleton loaders, background motion, ambient effects) by delegating them to Core Animation (iOS) and ObjectAnimator (Android) via react-native-ease.

Scope:

  • Identify existing Reanimated-based animations that are declarative, trigger-based, and do not depend on gesture input or layout properties
  • Replace those animations with the equivalent react-native-ease API
  • Test in a release build to confirm reduction in per-frame overhead
  • Enable Reanimated feature flags (ANDROID/IOS_SYNCHRONOUSLY_UPDATE_UI_PROPS) for remaining Reanimated animations

Out of scope: Gesture-driven animations (scroll-linked, drag, swipe), layout property animations (width/height), animations that need mid-flight JS interruption.

Dependencies: react-native-ease v0.7+, Expo SDK 55 / React Native 0.83+. Benchmarks referenced from the Expo blog article.

Acceptance criteria:

  • Identified animations render at ~0ms UI thread cost on iOS (verifiable with Xcode Instruments)
  • No visual regressions on the affected screens
  • Release build shows no frame drops on a mid-range Android device (Moto G8 class)
  • react-native-ease is added to package.json and usage is documented

Estimated effort: S

 

Copy/paste this prompt:

Implement the following React Native chunk for your mobile app: Migrate long-running ambient animations from Reanimated to react-native-ease to achieve ~0ms UI thread cost on iOS and significantly lower cost on Android.

Goal: For declarative, non-gesture animations (skeleton loaders, drifting backgrounds, ambient pulse effects), replace Reanimated with react-native-ease which delegates to Core Animation (iOS) and ObjectAnimator (Android) with zero per-frame JS involvement.

Files to create or modify:

  • package.json — add react-native-ease
  • Components with ambient animations (e.g., components/SkeletonLoader.tsx, screens/HomeScreen.tsx)

Step-by-step:

  1. Install: yarn add react-native-ease
  2. Audit your codebase for Reanimated animations that are:
    • Declarative (not driven by gestures or scroll position)
    • Animating only visual props: opacity, transform (translate, scale, rotate)
    • Long-running or looping (skeleton loaders, background motion)
  3. Replace each identified animation:
import { EaseView } from 'react-native-ease';

// Before (Reanimated)
<Animated.View style={animatedStyle} />

// After (react-native-ease)
<EaseView
  animation={{ type: 'translate', from: 0, to: 10, duration: 2000, loop: true }}
/>
  1. Test on both iOS (check Xcode Instruments → Display Link) and Android release build.
  2. For remaining Reanimated animations, enable feature flags in your Reanimated config:
    • ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS: true
    • IOS_SYNCHRONOUSLY_UPDATE_UI_PROPS: true

Acceptance criteria checklist:

  • react-native-ease installed and used for at least one ambient animation
  • No frame drops visible on a mid-range Android device in release build
  • iOS Instruments shows ~0ms display link callback for migrated animations
  • All animated screens pass a visual regression check
  • Reanimated feature flags enabled for remaining worklet animations

🟦 Chunk 3 — Optimize JSI Module: Replace HostObject with NativeState

Goal: Achieve up to 5x performance improvement on a custom JSI module by replacing the HostObject pattern with the more efficient HostFunction + NativeState architecture described in the Margelo benchmark series.

Scope:

  • Identify an existing custom JSI module using HostObject
  • Rewrite the native C++ layer to use Object + NativeState + HostFunction
  • Verify with a simple micro-benchmark (1M iterations) that the new version is faster
  • Update comments in the C++ file explaining the new pattern

Out of scope: Full module API redesign, switching to Nitro Modules, bridging new methods, Swift/Kotlin reimplementation.

Dependencies: An existing custom JSI module in the project; C++ development environment set up (Xcode / Android NDK).

Acceptance criteria:

  • Micro-benchmark (1M call loop) shows ≥3x speedup vs the HostObject version
  • No behavior regression — all existing JS-side tests pass
  • The new C++ implementation uses jsi::NativeState with thisVal.asObject(rt).getNativeState<T>(rt)
  • PR includes before/after benchmark numbers in the description

Estimated effort: M

 

Copy/paste this prompt:

Implement the following React Native chunk for your mobile app: Optimize a JSI module by replacing HostObject with NativeState for a 5x performance improvement.

Goal: Replace the HostObject::get() pattern with Object + NativeState + HostFunction to eliminate virtual dispatch, property name lookup, and string comparison overhead on every native call. Reference: https://blog.margelo.com/make-jsi-run-faster

Files to modify:

  • ios/MyModule.mm (or cpp/MyModule.cpp) — C++ JSI module implementation

Step-by-step:

  1. Identify the slow HostObject pattern:
// BEFORE: slow — HostObject::get() called on every property access
class MyModuleHostObject: public HostObject {
public:
  Value get(Runtime &rt, const PropNameID &name) override {
    if (name.utf8(rt) == "myMethod") {
      return Function::createFromHostFunction(...);
    }
  }
};
  1. Rewrite using NativeState + direct HostFunction:
// AFTER: fast — direct pointer access, no string comparison per call
class MyNativeState: public NativeState {
public:
  int myData = 0;
};

// In your module install function:
Object jsObject(rt);
jsObject.setNativeState(rt, std::make_shared<MyNativeState>());
jsObject.setProperty(rt, "myMethod",
  Function::createFromHostFunction(rt,
    PropNameID::forAscii(rt, "myMethod"), 0,
    [](Runtime &rt, const Value &thisVal, const Value *args, size_t count) {
      auto state = thisVal.asObject(rt).getNativeState<MyNativeState>(rt);
      return Value(state->myData);
    }));
rt.global().setProperty(rt, "MyModule", std::move(jsObject));
  1. Add an optional micro-benchmark:
auto t0 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; i++) { /* invoke */ }
auto t1 = std::chrono::high_resolution_clock::now();
  1. Run yarn ios and verify all existing JS-side calls work correctly.
  2. Run your JS test suite: yarn test

Acceptance criteria checklist:

  • HostObject pattern removed; NativeState pattern implemented
  • All existing JS tests pass without modification
  • No crashes on iOS simulator or Android emulator
  • Benchmark confirms ≥3x speedup (include numbers in PR description)
  • C++ file has comments explaining the NativeState pattern for future maintainers

🟦 Chunk 4 — Audit Native Dependencies for Swift Package Manager Compatibility

Goal: Prepare your project for the upcoming CocoaPods deprecation in React Native by auditing all native dependencies for SPM readiness and producing a migration plan document.

Scope:

  • List all native dependencies currently using CocoaPods (from package.json and ios/Podfile)
  • Check each dependency for SPM support (Package.swift presence in the repo)
  • Flag dependencies using C++/Swift direct interop (Nitro Modules ecosystem: VisionCamera, MMKV, Unistyles, Video) as "needs resolution"
  • Create docs/spm-migration-audit.md with a status table per dependency

Out of scope: Actually migrating to SPM, modifying any native dependency code, publishing SPM packages for your own modules.

Dependencies: None — this is a research and documentation task. RFC reference: https://github.com/react-native-community/discussions-and-proposals/pull/994

Acceptance criteria:

  • docs/spm-migration-audit.md created with a table: dependency name, version, has Package.swift, Nitro/C++ interop flag, SPM status, notes
  • Each dependency classified as: ✅ SPM ready / ⚠️ Partial / ❌ Not ready
  • Nitro-based libraries flagged with the C++/Swift interop limitation note from the RFC
  • Document committed and shared with the team

Estimated effort: S

 

Copy/paste this prompt:

Implement the following React Native chunk for your mobile app: Audit all native iOS dependencies for Swift Package Manager (SPM) compatibility ahead of the CocoaPods deprecation RFC.

Goal: Create a comprehensive audit document (docs/spm-migration-audit.md) classifying each native dependency as SPM-ready, partial, or not ready, so the team can plan migration before CocoaPods is deprecated.

Context: RFC #994 (https://github.com/react-native-community/discussions-and-proposals/pull/994) proposes replacing CocoaPods with SPM on iOS. Libraries using direct C++/Swift interop (Nitro Modules: react-native-vision-camera, react-native-mmkv, react-native-unistyles, react-native-video) need special attention as SPM does not yet support mixed C++/Swift targets.

Files to create:

  • docs/spm-migration-audit.md

Step-by-step:

  1. Open ios/Podfile and list every pod entry.
  2. Cross-reference with package.jsondependencies to identify all native modules.
  3. For each dependency, visit its GitHub repo and check for Package.swift at the root.
  4. Check if the library uses Nitro Modules or direct C++/Swift interop (search for HybridObject, NativeState, or bi-directional C++/Swift calls).
  5. Create docs/spm-migration-audit.md:
# SPM Migration Audit

RFC reference: https://github.com/react-native-community/discussions-and-proposals/pull/994

| Dependency | Version | Has Package.swift | Nitro/C++ Interop | SPM Status | Notes |
|---|---|---|---|---|---|
| react-native-vision-camera | 4.x | ✅ | ✅ | ⚠️ Partial | C++/Swift interop needs resolution |
| react-native-mmkv | 3.x | ✅ | ✅ | ⚠️ Partial | Same C++/Swift interop issue |
| react-native-reanimated | 4.x | ❌ | ✅ | ❌ Not ready | Podspec-based build scripts |

## Next Steps
- Monitor: RFC PR #994
- Track each library's SPM issue/PR
- Re-audit when RN core ships SPM support

Acceptance criteria checklist:

  • All native dependencies from ios/Podfile listed in the table
  • Each has a SPM readiness status (✅ / ⚠️ / ❌)
  • Nitro-based/C++ interop libraries explicitly flagged
  • Document committed to docs/spm-migration-audit.md
  • Shared in team channel or included in a PR for visibility

⚛️ Chunk 5 — Enable React Compiler in Your React Native Project

Goal: Automatically eliminate unnecessary re-renders across your component tree by enabling the React Compiler, which is now production-tested at Meta (18-month retrospective) and actively being ported to Rust for even faster compilation.

Scope:

  • Install babel-plugin-react-compiler and configure it in babel.config.js
  • Enable compilation on a single feature screen (not the entire app) as a pilot
  • Verify with React DevTools Profiler that re-render counts drop on the target screen
  • Remove manual useMemo/useCallback/React.memo that are now redundant

Out of scope: Full codebase migration, fixing all compiler opt-out cases, TypeScript strict mode changes, enabling on shared/utility components.

Dependencies: React 19+, Babel 7+, React Native 0.76+ (New Architecture). Check your Expo SDK version for compatibility.

Acceptance criteria:

  • babel-plugin-react-compiler installed and configured without errors
  • At least one screen compiles successfully with zero use no memo directives
  • React DevTools Profiler shows reduced re-render count on the target screen during a typical user interaction
  • All existing CI tests pass

Estimated effort: S

 

Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Enable the React Compiler (babel-plugin-react-compiler) to automatically memoize components and eliminate unnecessary re-renders.

Goal: Let the React Compiler automatically apply memoization to pure components, replacing manual useMemo/useCallback/React.memo. The compiler has been production-tested at Meta for 18 months and is safe to adopt incrementally.

Files to create or modify:

  • package.json — add babel-plugin-react-compiler as a dev dependency
  • babel.config.js — register the compiler plugin scoped to one directory
  • Target screen file (e.g., screens/FeedScreen.tsx) — validate it compiles cleanly

Step-by-step:

  1. Install: yarn add -D babel-plugin-react-compiler
  2. Update babel.config.js to scope compilation to one screen:
module.exports = {
  presets: ['babel-preset-expo'],
  plugins: [
    ['babel-plugin-react-compiler', {
      sources: (filename) => filename.includes('screens/FeedScreen'),
    }],
  ],
};
  1. Clear Metro cache: npx expo start --clear
  2. Open the target screen and interact with it normally.
  3. Open React DevTools → Profiler → record the interaction.
  4. Verify that child components that don't receive new props are no longer highlighted (grey = not re-rendered).
  5. If you see use no memo in the output, review those components for side effects or missing hook dependencies.
  6. Remove any now-redundant React.memo(...), useMemo, or useCallback wrappers from the compiled screen.
  7. Once validated, expand sources to cover more directories incrementally.

Acceptance criteria checklist:

  • babel-plugin-react-compiler installed as a dev dependency
  • babel.config.js updated for at least one screen/directory
  • Metro bundler starts without errors after cache clear
  • React DevTools Profiler confirms fewer re-renders on the target screen
  • No runtime exceptions introduced (all existing unit/E2E tests pass)
  • Manual React.memo/useMemo/useCallback removed where compiler now handles it