React Native Roadmap 2026-W18
Week of April 27–May 3, 2026
Items This Week
| # | Title | Label | Link |
|---|---|---|---|
| 1 | EAS Gradle cache for Android builds | 🟧 EXPO | Read |
| 2 | The Real Cost of React Native Animations: Benchmarking Every Approach | 🟦 RN | Read |
| 3 | Part 1: How to Make Pure JSI Code Faster in React Native | 🟦 RN | Read |
| 4 | RFC: Swift Package Manager support — CocoaPods deprecation | 🟦 RN | Read |
| 5 | React Compiler 18-month retrospective & Rust rewrite tested at Meta | ⚛️ REACT | Read |
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 ineas.json - Verify cache is active by inspecting the "Run Gradle" step in EAS build logs for
FROM CACHEmarkers - 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 CACHEon the second run - Build time on a repeat build is at least 30% faster than a clean build
eas.jsonchange 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— addEAS_GRADLE_CACHE: "1"env var to your build profiles (production, preview, development)
Step-by-step:
- Open
eas.jsonand locate thebuildsection. - For each Android profile (e.g.,
production,preview), add:
"env": {
"EAS_GRADLE_CACHE": "1"
}
- Commit and push the change.
- Trigger an EAS build:
eas build --platform android --profile production - After the first build completes, trigger a second build immediately (no code changes).
- Open the second build logs → "Run Gradle" step → verify tasks show
FROM CACHE.
Acceptance criteria checklist:
-
eas.jsonupdated withEAS_GRADLE_CACHE: "1"in all relevant profiles - Second EAS build shows
FROM CACHEin 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-easeAPI - 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-easeis added topackage.jsonand 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— addreact-native-ease- Components with ambient animations (e.g.,
components/SkeletonLoader.tsx,screens/HomeScreen.tsx)
Step-by-step:
- Install:
yarn add react-native-ease - 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)
- 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 }}
/>
- Test on both iOS (check Xcode Instruments → Display Link) and Android release build.
- For remaining Reanimated animations, enable feature flags in your Reanimated config:
ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS: trueIOS_SYNCHRONOUSLY_UPDATE_UI_PROPS: true
Acceptance criteria checklist:
-
react-native-easeinstalled 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::NativeStatewiththisVal.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(orcpp/MyModule.cpp) — C++ JSI module implementation
Step-by-step:
- 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(...);
}
}
};
- 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));
- 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();
- Run
yarn iosand verify all existing JS-side calls work correctly. - Run your JS test suite:
yarn test
Acceptance criteria checklist:
-
HostObjectpattern removed;NativeStatepattern 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.jsonandios/Podfile) - Check each dependency for SPM support (
Package.swiftpresence 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.mdwith 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.mdcreated with a table: dependency name, version, hasPackage.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:
- Open
ios/Podfileand list everypodentry. - Cross-reference with
package.json→dependenciesto identify all native modules. - For each dependency, visit its GitHub repo and check for
Package.swiftat the root. - Check if the library uses Nitro Modules or direct C++/Swift interop (search for
HybridObject,NativeState, or bi-directional C++/Swift calls). - 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/Podfilelisted 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-compilerand configure it inbabel.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.memothat 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-compilerinstalled and configured without errors- At least one screen compiles successfully with zero
use no memodirectives - 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— addbabel-plugin-react-compileras a dev dependencybabel.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:
- Install:
yarn add -D babel-plugin-react-compiler - Update
babel.config.jsto scope compilation to one screen:
module.exports = {
presets: ['babel-preset-expo'],
plugins: [
['babel-plugin-react-compiler', {
sources: (filename) => filename.includes('screens/FeedScreen'),
}],
],
};
- Clear Metro cache:
npx expo start --clear - Open the target screen and interact with it normally.
- Open React DevTools → Profiler → record the interaction.
- Verify that child components that don't receive new props are no longer highlighted (grey = not re-rendered).
- If you see
use no memoin the output, review those components for side effects or missing hook dependencies. - Remove any now-redundant
React.memo(...),useMemo, oruseCallbackwrappers from the compiled screen. - Once validated, expand
sourcesto cover more directories incrementally.
Acceptance criteria checklist:
-
babel-plugin-react-compilerinstalled as a dev dependency -
babel.config.jsupdated 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/useCallbackremoved where compiler now handles it