Skip to content
chornous.dev

Type two or more letters. Esc closes.

Index of sheets
Theme

Sheet 03 · Writing

All notes

React Native 0.88: events dispatch through EventTarget, Text gets variable fonts, and iOS apps move to SceneDelegate

React Native 0.88 shipped on October 19, 2026 with React 19.3.0 and Metro 0.87. Events now run through the W3C EventTarget path, fontVariationSettings works on Text and TextInput, and RCTReactNativeFactory gains SceneDelegate entry points ahead of Apple's iOS 27 SDK requirement. No user-facing breaking changes.

Pl. 33 · tree drawing generated from the slug “react-native-0-88-event-target-scenedelegate”

The React Native team released 0.88 on October 19, 2026. It bundles React 19.3.0, Metro 0.87 and Hermes 260318099.0.4, and it's the third release with no user-facing breaking changes, after 0.83 and 0.86. The release has 568 commits from 91 contributors. With 0.88 out, 0.85 moves to unsupported.

For most apps the upgrade from 0.87 needs no code changes. The release still deserves an afternoon, for two reasons: the event system underneath your handlers changed, and Apple's next SDK forces a lifecycle migration on iOS.

Events run through EventTarget

React Native now dispatches events through the W3C EventTarget API by default. The plugin-based dispatcher the renderer used since its first years sits behind an internal flag in 0.88, and the team plans to remove it. The announcement calls this the largest runtime change in the release. The React 19.3.0 bump unblocked it.

Events now travel a capture path down to the target and bubble back up through real event targets. capture, stopPropagation() and preventDefault() follow the DOM model instead of a React Native copy of it.

Your props don't change. onPress and friends keep their names, receive the same synthetic events with nativeEvent and persist(), and fire in the same order. onLayout and other direct events still fire on the target alone. The touch responder system stays as it was.

The code at risk reaches into React Native's event internals: libraries that registered plugins or patched the old dispatcher. The team asks you to report anything that works on the legacy path and breaks on the new one.

On the Canary channel, native refs expose the imperative API:

const ref = useRef(null);

useEffect(() => {
  const onClick = event => {
    // ...
  };
  ref.current?.addEventListener('click', onClick);
  return () => ref.current?.removeEventListener('click', onClick);
}, []);

If an element has both a prop and an imperative listener for one event, the prop runs first.

Variable fonts

fontVariationSettings works on <Text> and <TextInput> on Android and iOS. It accepts the CSS string and an object:

<Text style={{fontVariationSettings: {wght: 700, slnt: -10}}}>Object form</Text>
<Text style={{fontVariationSettings: "'wght' 700, 'slnt' -10"}}>String form</Text>

Keys are four-character axis tags with finite numbers. If you load several static weights of one family to fake a variable font, you can ship one file instead.

iOS: SceneDelegate before Xcode 27

Starting with the iOS 27 SDK, Apple requires the UIScene lifecycle, and UIKit apps built with Xcode 27 that skip it fail to launch. React Native 0.88 adds support without removing the AppDelegate path. RCTReactNativeFactory gets two entry points:

  • startReactNativeWithModuleName:inWindow:connectionOptions:
  • startReactNativeWithModuleName:inWindow:initialProperties:connectionOptions:

The migration has three parts:

Moving a React Native 0.88 app to SceneDelegate
StepWhere
Move the React Native entry point from AppDelegate to a SceneDelegateios/YourApp/SceneDelegate.swift
Forward the scene's URL and user-activity callbacks to RCTLinkingManagerSceneDelegate.swift
Add a UIApplicationSceneManifest keyInfo.plist

The community template already has both files, so diff against it in the Upgrade Helper. Support covers a single scene: keep UIApplicationSupportsMultipleScenes set to false. At launch, React Native forwards the first URL context or user activity from the connection options and drops the rest. Under UIScene the window can resize, and Dimensions.get() and useWindowDimensions() report the current values. Test deep links that arrive while the app is cold.

Swift Package Manager and CocoaPods

SwiftPM support stays experimental and opt-in in 0.88. The team published a schedule:

  1. 0.87

    Experimental Swift Package Manager support arrives.
  2. 0.88

    Scripts hardened for more libraries and edge cases.
  3. 0.89

    SwiftPM becomes the target default, with a CocoaPods opt-out.

The team plans to drop CocoaPods in a later release. Library authors can now declare SwiftPM settings in a swiftpmConfig section of package.json. The SwiftPM block in react-native.config.js is deprecated.

Deprecations and quieter changes

  • react-native/Libraries/Core/InitializeCore is deprecated. Import react-native/setup-env.
  • backgroundSize, backgroundPosition and backgroundRepeat lose their experimental_ prefix. The old names keep working as aliases.
  • InputAccessoryView on iOS is deprecated. The team points to KeyboardStickyView from react-native-keyboard-controller.
  • Android's default User-Agent now includes your app name and version, as iOS does. A server that keys off the old string will see a new one.
  • Java TurboModules take and return ArrayBuffer. Borrowed buffers in synchronous methods expire when the method returns, so copy the bytes if you keep them.

This week

  1. Upgrade a branch from 0.87 with the Upgrade Helper and run your test suite. Expo projects get 0.88 with SDK 58.
  2. Grep your dependencies for imports from React Native's event plugin internals, and file issues against libraries that use them.
  3. Start the SceneDelegate migration now, before Xcode 27 makes it a launch blocker, and test cold-start deep links on a device.
  4. Check your API logs and analytics filters for rules that match the old Android User-Agent.
  5. If you're on 0.85, schedule the jump. It no longer gets fixes.

Volodymyr Chornous