React Native Roadmap 2026-W21
Week of May 18–May 24, 2026
Items This Week
| # | Title | Label | Link |
|---|---|---|---|
| 1 | Expo SDK 56 — RN 0.85, Expo UI stable, inline modules, faster builds | 🟧 EXPO | Read |
| 2 | Hermes-node — Hermes CLI compatible with Node.js APIs | 🟦 RN | Read |
| 3 | WTF does .box() do in Nitro Modules? | 🟦 RN | Read |
| 4 | Hot Updater — self-hosted OTA updates with bundle diffing | 🟦 RN | Read |
| 5 | Storybook 10.4 — React Native isolation & agentic setup | ⚛️ REACT | Read |
| 6 | Relay 21.0 — first-party TypeScript support + experimental RSC | ⚛️ REACT | Read |
5-Day Action Plan
🟧 Chunk 1 — Upgrade to Expo SDK 56
Goal: Bring your project to Expo SDK 56 to unlock React Native 0.85, Hermes v1 as default engine, stable Expo UI, TypeScript 6, and build-time improvements of ~16% on iOS.
Scope:
- Run
npx expo install expo@^56.0.0 --fixto align all dependencies with SDK 56 - Run
npx expo-doctor@latestand fix any reported issues - Update iOS deployment target from
15.1to16.4in your podspec (for custom native modules) - Handle breaking changes:
expo/fetchis now the default global fetch — remove manual imports - Handle async
copy()/move()breaking change inexpo-file-system— replace withcopySync()/moveSync()where synchronous behaviour is needed - Run the
@expo/vector-iconscodemod:npx @react-native-vector-icons/codemodto migrate to@react-native-vector-icons/*scoped packages - Rebuild the native project and test on both iOS and Android simulators
Out of scope: Adoption of inline modules, Expo Modules type generation (expo-type-information), brownfield multi-framework setup.
Dependencies: None — this is the foundation for subsequent chunks.
Acceptance criteria:
- App boots and renders correctly on iOS 16.4+ and Android
npx expo-doctor@latestreports no critical errors- No
@expo/vector-iconsimport errors in the build - iOS clean build time is measurably reduced (target: ≥ 50 s saved vs SDK 55)
Estimated effort: M
**Copy/paste this prompt:**
Implement the following React Native chunk for your mobile app: Upgrade the project from Expo SDK 55 to Expo SDK 56.
Goal: Align all dependencies with Expo SDK 56, apply breaking-change fixes, and verify the app compiles and runs on both platforms.
Files to create or modify:
package.json— bumped Expo and peer dependency versionsapp.json/app.config.ts— update any SDK-version-specific configpodspec(custom native modules only) — bump iOS deployment target to16.4- All files importing from
@expo/vector-icons— migrate to@react-native-vector-icons/* - All files using synchronous
copy()/move()fromexpo-file-system— switch tocopySync()/moveSync()
Step-by-step instructions:
- Run
npx expo install expo@^56.0.0 --fixand commit the resulting lockfile change. - Run
npx expo-doctor@latestand resolve every reported issue before continuing. - Search for
from '@expo/vector-icons'across the codebase and runnpx @react-native-vector-icons/codemodto auto-migrate. - Search for
.copy(and.move(calls fromexpo-file-systemand replace asynchronous usages correctly; usecopySync/moveSynconly where truly synchronous behaviour is required. - If you have custom native modules, update the iOS deployment target to
16.4in every.podspecfile. - Delete
ios/andandroid/generated directories (if using CNG / Continuous Native Generation) and re-runnpx expo prebuild. - Run
npx expo run:iosandnpx expo run:androidand verify the app launches without errors.
Acceptance criteria checklist:
-
npx expo-doctor@latestexits with 0 errors - App launches on iOS 16.4 simulator without crash
- App launches on Android emulator without crash
- No remaining
@expo/vector-iconsimports in source -
expo/fetchis used as the global fetch without manual imports
🟦 Chunk 2 — Implement Hot Updater for Self-Hosted OTA Updates
Goal: Deploy JavaScript-only changes to production without an app store submission, using Hot Updater's self-hosted OTA with bundle diffing to reduce update payload size by up to 58%.
Scope:
- Install
hot-updaterand its server-side package - Configure a storage provider (S3-compatible bucket or a lightweight Node server) to host bundles
- Enable Hermes bytecode bundle diffing in Hot Updater config
- Add the in-app update check (foreground trigger) with a non-blocking UX
- Wire a
publishscript into your CI pipeline (GitHub Actions or similar) that runs on merge tomain
Out of scope: Native code updates, EAS Build integration, rollback UI, user-facing update progress bar.
Dependencies: Expo SDK 56 (Chunk 1) is recommended; Hot Updater also works standalone with bare React Native.
Acceptance criteria:
- A JS-only change (e.g., a button label) is pushed via
hot-updater publishand appears on device without a new binary release - The published bundle diff is verifiably smaller than the full bundle in CI logs
- Update check fires on app foreground and silently applies the patch on next restart
- The app falls back gracefully if the update server is unreachable
Estimated effort: M
**Copy/paste this prompt:**
Implement the following React Native chunk for your mobile app: Set up Hot Updater for self-hosted Over-the-Air (OTA) updates with bundle diffing.
Goal: Allow JS-only changes to reach production devices without an app store submission.
Files to create or modify:
package.json— addhot-updaterdependencyapp.json/app.config.ts— add the Hot Updater config pluginApp.tsx(or entry point) — wrap the app withHotUpdater.wrapand configure the update check.github/workflows/publish.yml— add a CI job that runshot-updater publishon merge tomainhot-updater.config.ts(new file) — define storage provider, bundle diffing enabled
Step-by-step instructions:
- Install:
npx expo install hot-updater. - Add
hot-updaterto thepluginsarray inapp.jsonwith your storage provider credentials. - Create
hot-updater.config.tsat the project root, setbundleDiff: trueand configure your S3 bucket or compatible provider. - Wrap your entry component:
export default HotUpdater.wrap({ source: 'https://your-bucket/updates' }, App). - Add a foreground-check trigger using
AppStateto callHotUpdater.checkForUpdate()when the app comes to the foreground. - Add a GitHub Actions step: after all tests pass on
main, runnpx hot-updater publish --platform ios --platform android. - Test locally: make a visible JS change, run
npx hot-updater publish, install a previous build on a simulator, launch it and verify the update is applied.
Acceptance criteria checklist:
-
hot-updater publishcompletes without errors in CI - Bundle diff size logged in CI is smaller than full bundle size
- A JS-only change appears on device without a new binary
- App renders normally when the update server returns 404 (no update available)
- App renders normally when the update server is unreachable (network off)
🟧 Chunk 3 — Migrate Community Components to Expo UI Drop-in Replacements
Goal: Replace a set of popular community UI libraries with Expo UI's stable, SwiftUI/Jetpack Compose-backed drop-in replacements to reduce bundle size, eliminate legacy UIKit wrappers, and gain production-ready native fidelity.
Scope:
- Audit the codebase for usage of:
@react-native-community/datetimepicker,@gorhom/bottom-sheet,@react-native-picker/picker,@react-native-community/slider,@react-native-segmented-control/segmented-control - Replace each import with its
@expo/ui/community/*equivalent (in most cases, only the import path changes) - Document any props that are unsupported or behave differently in Expo UI and adjust accordingly
- Validate on both iOS and Android simulators
Out of scope: Web support (Expo UI web APIs are still experimental), custom theming beyond platform defaults, SwiftUI useNativeState integration.
Dependencies: Expo SDK 56 (Chunk 1).
Acceptance criteria:
- All migrated component usages render pixel-correctly on iOS and Android
- No remaining imports from
@gorhom/bottom-sheet,@react-native-community/datetimepicker,@react-native-picker/picker npx expo-doctorreports no peer-dependency conflicts from removed librariespackage.jsonno longer lists replaced libraries as dependencies
Estimated effort: S
**Copy/paste this prompt:**
Implement the following React Native chunk for your mobile app: Migrate community UI components to Expo UI drop-in replacements available in SDK 56.
Goal: Swap heavy community wrappers for Expo UI's native SwiftUI/Jetpack Compose-backed equivalents by changing only the import path.
Files to create or modify:
- All files importing from
@react-native-community/datetimepicker→ replace with@expo/ui/community/datetime-picker - All files importing from
@gorhom/bottom-sheet→ replace with@expo/ui/community/bottom-sheet - All files importing from
@react-native-picker/picker→ replace with@expo/ui/community/picker - All files importing from
@react-native-community/slider→ replace with@expo/ui/community/slider - All files importing from
@react-native-segmented-control/segmented-control→ replace with@expo/ui/community/segmented-control package.json— remove replaced libraries from dependencies after migration
Step-by-step instructions:
- Run a global search for each community library import across
src/,app/, andcomponents/. - For each match, change only the import path to the
@expo/ui/community/*equivalent. Do not change component names or props in the first pass. - Build the app (
npx expo run:ios && npx expo run:android) and check for TypeScript errors related to unsupported props. - For any unsupported prop, check the Expo UI SDK 56 docs and either remove the prop, find the equivalent, or add a TODO comment for follow-up.
- Manually test each migrated component on both platforms in a representative user flow.
- Remove the now-unused community libraries from
package.jsonand runnpx expo install --fix.
Acceptance criteria checklist:
- No build errors related to replaced component imports
-
DateTimePickeropens and returns a valid date on iOS and Android -
BottomSheetanimates open and closed without jitter -
PickerandSliderrespond to user input correctly -
package.jsonno longer lists replaced packages
⚛️ Chunk 4 — Set Up Storybook 10.4 with React Native Isolation
Goal: Upgrade to Storybook 10.4 and enable the new cleaner React Native isolation mode to develop and review UI components independently from business logic and backend dependencies.
Scope:
- Upgrade Storybook to 10.4 using
npx storybook@latest upgrade - Configure
@storybook/react-nativewith the improved 10.4 setup (reduced boilerplate, no manual register step) - Write 3 component stories for key UI components (e.g.,
Button,Card,TextInput) - Configure TanStack Query mock provider in
.storybook/preview.tsxif TanStack Query is used - Verify the Storybook app launches in the Expo dev client
Out of scope: Chromatic visual regression testing, CI integration, Storybook Web build.
Dependencies: None — Storybook can be set up independently of the SDK upgrade.
Acceptance criteria:
- Storybook launches in the Expo dev client on both iOS and Android without errors
- All 3 component stories render correctly in isolation (no network calls, no navigation context required)
- Switching between stories updates the rendered component without a full app reload
- The isolation mode correctly mocks providers so components are self-contained
Estimated effort: S
**Copy/paste this prompt:**
Implement the following React Native chunk for your mobile app: Upgrade to Storybook 10.4 and configure React Native isolation mode.
Goal: Enable isolated component development with Storybook 10.4's new React Native setup.
Files to create or modify:
package.json— bump@storybook/react-nativeto^10.4.0and related addons.storybook/main.ts— update configuration to use the 10.4 framework preset.storybook/preview.tsx— configure global decorators (ThemeProvider, QueryClientProvider, NavigationContainer mock)src/components/Button/Button.stories.tsx(new) — 3 stories: Default, Disabled, Loadingsrc/components/Card/Card.stories.tsx(new) — 3 stories: Basic, WithImage, Skeletonsrc/components/TextInput/TextInput.stories.tsx(new) — 3 stories: Empty, WithValue, Error state
Step-by-step instructions:
- Run
npx storybook@latest upgradeand follow the migration prompts. - In
.storybook/main.ts, ensure the framework is set to@storybook/react-nativeand theaddonslist includes@storybook/addon-ondevice-controls. - In
.storybook/preview.tsx, wrap all stories in a decorator that provides: a mockQueryClient, a mockNavigationContainer, and your app'sThemeProvider. - Create
Button.stories.tsxwithDefault,Disabled, andLoadingstories usingargTypesfor interactive controls. - Create
Card.stories.tsxandTextInput.stories.tsxsimilarly. - Run the Storybook entry point via Expo dev client:
STORYBOOK_ENABLED=true npx expo startand verify stories load.
Acceptance criteria checklist:
- Storybook app launches without JS errors on iOS and Android
-
Buttonstories show 3 variants controllable via the addon panel -
Cardstories show 3 variants without network calls -
TextInputstories show error state styling without a running API - Switching stories does not cause a full app reload
🟦 Chunk 5 — Use .box() in Nitro Modules for Worklet-Crossing Native State
Goal: Leverage the .box() API in Nitro Modules to transfer NativeState-backed objects across worklet runtime boundaries, enabling synchronous, zero-serialization communication between native and the UI thread.
Scope:
- Read and understand the
.box()/NativeState/HostObjectmodel (source article) - Identify a concrete use case in your app: for example, a camera state controller, audio session state, or animation value holder
- Implement or extend a Nitro module to expose a
NativeStateobject that can be.box()-ed and passed to a Reanimated worklet - Test synchronous access from a worklet (e.g., reading a native value during a gesture handler) on both platforms
Out of scope: Creating a brand-new full Nitro module from scratch, SwiftUI useNativeState integration, publishing the module as an npm package.
Dependencies: React Native New Architecture (mandatory for Nitro Modules); Reanimated 3.x for worklet testing.
Acceptance criteria:
- A
NativeStateobject is boxed with.box()and accessible from a Reanimated worklet without JS-thread serialization - Reading the boxed state from a gesture handler produces no UI jitter or lag
- No crashes on rapid gesture interactions on iOS and Android
- The worklet can read the native value synchronously and use it to drive an animation
Estimated effort: M
**Copy/paste this prompt:**
Implement the following React Native chunk for your mobile app: Use .box() in Nitro Modules to pass NativeState objects across worklet runtime boundaries.
Goal: Enable synchronous, zero-serialization native state access from Reanimated worklets using Nitro Modules' .box() API.
Files to create or modify:
modules/MyNitroModule/src/MyNitroModule.nitro.ts— declare a method returning aNativeStatethat exposes a.box()methodmodules/MyNitroModule/ios/MyNitroModule.swift— implement the Swift NativeState class and the.box()returnmodules/MyNitroModule/android/MyNitroModule.kt— implement the Kotlin equivalentsrc/hooks/useBoxedNativeState.ts(new) — a React hook that calls the Nitro module and holds the boxed referencesrc/components/MyAnimatedComponent.tsx— a component that passes the boxed state to a Reanimated worklet viauseSharedValueorrunOnUI
Step-by-step instructions:
- In your Nitro module spec (
*.nitro.ts), add a methodgetState(): NativeState<MyState>whereMyStateincludes the value(s) you want to share. - Implement
MyStatein Swift/Kotlin so it conforms to the NitroNativeStateprotocol, exposing the relevant fields. - In the Nitro module host method, return the
NativeStatewrapped via.box()so it becomes aHostObject. - In
useBoxedNativeState.ts, callMyNitroModule.getState()on mount and store the returnedHostObjectin auseSharedValue. - In
MyAnimatedComponent.tsx, useuseAnimatedGestureHandleroruseAnimatedStyleand read the boxed state's fields directly inside the worklet callback. - Run the component on a physical device (or high-fidelity simulator) and perform rapid gestures to verify no UI jitter.
Acceptance criteria checklist:
- The Nitro module compiles on both iOS and Android with no errors
- The boxed
NativeStateis accessible from inside arunOnUIworklet - Reading a native value from the worklet does not cause frame drops (check with Reanimated FPS overlay)
- No crash occurs after 30+ rapid gesture interactions on iOS
- No crash occurs after 30+ rapid gesture interactions on Android