← All posts

studioLightning Studio

React Native 0.88 and Expo SDK 58: Your Upgrade Plan for iOS 27

React Native 0.88 and Expo SDK 58: Your Upgrade Plan for iOS 27

React Native 0.88 landed on October 19, 2026, four days after Expo SDK 58, and together they are the first React Native stack built for the iOS 27 SDK. The headline says "no breaking changes", and for most JavaScript code that is true. The work that matters sits in native files: a new SceneDelegate entry point, a resizable window on iPhone, and a package manager switch that is coming whether you plan for it or not. This guide explains what changed and gives you an upgrade order that keeps release builds shippable.

▶ Watch the video version (about 4 minutes):

What React Native 0.88 actually ships

The official release post lists five highlights:

  • Web-based event dispatching. Events now flow through the W3C EventTarget model, with a real capture phase down to the target and bubbling back up. This is the biggest runtime change in the release and was unblocked by the move to React 19.3.0.
  • Variable fonts. fontVariationSettings works on <Text> and <TextInput> on both Android and iOS, in CSS string form or as an object such as {wght: 700, slnt: -10}.
  • SceneDelegate support on iOS. New entry points on RCTReactNativeFactory let you start React Native from a UIWindowScene instead of the AppDelegate.
  • ArrayBuffer for Java TurboModules. Binary data can cross into Java without Base64 or number-array encoding. Synchronous methods can even borrow JavaScript memory without a copy.
  • Swift Package Manager, still experimental. The npx react-native spm tooling from 0.87 was hardened against real apps and libraries.

Under the hood it also bumps Metro to 0.87 (minimum 0.87.1) and Hermes to a new build. The release has 568 commits from 91 contributors. It is the third release, after 0.83 and 0.86, that the core team labels as having no user-facing breaking changes. If your app already runs on 0.87, the team says you should be able to move to 0.88 without changing app code.

One support note: with 0.88 out, 0.85.x is now unsupported. If you are still on 0.85 or older, you are outside the window that receives fixes.

The iOS 27 deadline behind SceneDelegate

The SceneDelegate work is the reason this release matters more than its changelog suggests. As the React Native team puts it, starting with the iOS 27 SDK Apple requires the UIScene life cycle, and UIKit apps built with Xcode 27 that do not adopt it will fail to launch. We covered the native side of this in our Xcode 27 UIScene migration guide. React Native 0.88 is the version that gives you an official path.

The change is additive. The old AppDelegate path keeps working, so you can upgrade first and migrate the entry point as a separate commit. When you migrate, you need to:

  1. Move the React Native start-up code from AppDelegate into a new SceneDelegate.
  2. Forward the scene's URL and user-activity callbacks to RCTLinkingManager, or deep links and universal links will silently stop working.
  3. Add a UIApplicationSceneManifest key to Info.plist.

The community template already contains a working SceneDelegate.swift, and the Upgrade Helper shows the exact diff between your version and 0.88.

Limits you should know about

  • Only one scene is supported in 0.88. Keep UIApplicationSupportsMultipleScenes set to false.
  • At launch, React Native forwards only the first URL context or user activity from the scene's connection options. If your app can be opened with several URLs at once, test that case.
  • With the scene life cycle the app window is resizable. Dimensions.get() and useWindowDimensions() now update with the current size, so any layout that reads the screen size once at start-up can break.

That last point is where most of our own QA time goes. Hard-coded widths, one-time size reads in a module scope and orientation assumptions all show up as clipped layouts on a resized window.

Expo SDK 58: same engine, more decisions

Expo SDK 58, published on October 15, is "built for iOS 27" and moves Expo apps from React Native 0.86 to 0.88, so it carries the changes from both 0.87 and 0.88. Expo apps already use the scene-based life cycle, so the template handles the launch problem for you. If you customise AppDelegate.swift, Expo points you to its scene life cycle migration guide because the delivery of app delegate callbacks changes.

Several SDK 58 changes deserve a line in your upgrade ticket:

  • Orientation. On iOS 27, requireFullScreen no longer opts your app out of resizing, and ScreenOrientation.lockAsync may have no effect while the app is resizable. Portrait-only apps need real layout testing at other sizes.
  • R8 on by default. The Android template now enables R8 in release builds. Code that relies on reflection may need ProGuard keep rules, or you can opt out with expo-build-properties.
  • Removed React Native APIs. InteractionManager (use requestIdleCallback), the Touchable root export, the Modal animated prop and several deprecated StatusBar props are gone.
  • Library behaviour changes. File.write() in expo-file-system is now async, expo-notifications shows foreground notifications by default, and Expo Router projects default to server output on web.
  • Expo Go lags behind. The SDK 58 build of Expo Go reaches the stores "within a few weeks". Until then, npx create-expo-app still creates SDK 57 projects; use --template default@sdk-58 and a development build.

There is also a small tooling wrinkle: a GitHub issue reports that expo-doctor on SDK 58.0.21 asks for React Native 0.88.0 while the template ships 0.88.1. Do not downgrade just to silence that warning; the precompiled modules were rebuilt against 0.88.1.

The 0.87 changes you may have skipped

Teams jumping from 0.86 (or Expo SDK 57) also take on everything from React Native 0.87, released on August 11, and that release was not breaking-change free:

  • Strict TypeScript API by default. Deep imports such as react-native/Libraries/* are now type errors, and refs use instance types like ViewInstance and TextInputInstance. A temporary opt-out exists, but Expo notes it is set to be removed after React Native 0.89.
  • Higher toolchain floor. Node.js 22, Android Gradle Plugin 9 and Kotlin 2.0 or newer are now required.

In our experience, the TypeScript errors are mostly mechanical, while the AGP 9 jump causes the real surprises in older native modules and custom Gradle scripts. Update CI images before you touch the app.

Swift Package Manager: the next forced move

CocoaPods still works in 0.88, but the roadmap is explicit:

VersionSwift Package Manager statusWhat you should do
0.87Experimental, opt-inTry it on a branch
0.88Hardened scripts, more librariesRun npx react-native spm and log blockers
0.89Planned default, CocoaPods opt-outBe ready or set the opt-out
LaterCocoaPods support removedMust be on SwiftPM

0.88 fixes several practical issues: the generated manifests read the minimum iOS version from your app instead of a hard-coded iOS 15, Xcode builds no longer break for libraries that ship their own Package.swift, and libraries can declare SwiftPM settings in a swiftpmConfig section of package.json. Expo SDK 58 has restructured its iOS sources into separate SwiftPM targets and ships an autolinking plugin as a preview. The React Native team collects blockers in RFC #0994. If one of your native dependencies is unmaintained, now is the time to find out, not when 0.89 flips the default.

A practical upgrade order

This is the sequence we use for client apps. Each step is a separate pull request with its own TestFlight and internal testing build.

  1. Toolchain first. Node 22, AGP 9, Kotlin 2.0+, Xcode 27 on CI. Build the current app unchanged.
  2. Bump React Native or the Expo SDK. Bare apps: follow the Upgrade Helper diff to 0.88. Expo apps: npx expo install expo@^58.0.0 --fix, then read the Expo Router migration guide if you use it.
  3. Fix types and removed APIs. Clear deep imports, replace InteractionManager and check StatusBar usage.
  4. Migrate to SceneDelegate (bare apps) or review custom AppDelegate code (Expo apps). Test cold start, deep links, universal links, push notification taps and state restoration.
  5. Test resizable layouts. Rotate, resize and check every screen that reads dimensions. Do not rely on orientation locks.
  6. Android release build with R8. Run the release variant on a real device and check crash reports for missing classes.
  7. Try SwiftPM on a branch. Keep CocoaPods for production until every dependency builds.

Pre-release checklist

  • [ ] App launches when built with Xcode 27
  • [ ] Deep links work from cold start and from background
  • [ ] Layout survives a window resize on iPhone and iPad
  • [ ] Android release build tested with R8 enabled
  • [ ] Server-side logs adjusted for the new Android User-Agent string (it now includes the app name and version)
  • [ ] Deprecated imports such as InitializeCore replaced with react-native/setup-env

Our take

For a small team, 0.88 is a good release to land on. It is the first version where the iOS 27 launch requirement has an official React Native answer, it has no new breaking changes over 0.87, and it buys time before SwiftPM becomes the default. We would not wait for 0.89: that release is planned to change the iOS dependency setup, and you do not want to debug a package manager switch and a life cycle migration in the same sprint.

The features we will use first are less dramatic than the event system rewrite. Variable fonts remove a pile of font files from our bundles, and ArrayBuffer in Java TurboModules simplifies modules that move images or audio between JavaScript and native code. The web-style event model should be invisible for most apps; only code that reaches into React Native's internal event plugins needs a test pass.

Sources

Need help moving a React Native or Expo app to 0.88 and iOS 27? Lightning Studio builds and maintains cross-platform apps from Da Nang. Write to support@lightning-studio.org or see what we do at https://lightning-studio.org/studio/.


Cover photo: Laptop Programmcode by Negative Space, CC0 1.0, via Wikimedia Commons (cropped).

Facebook