From f3f113f72564e6241a0e5e5f632263e840d40b5f Mon Sep 17 00:00:00 2001 From: tido64 <4123478+tido64@users.noreply.github.com> Date: Thu, 29 Oct 2026 09:20:16 +0000 Subject: [PATCH] fix: declare support for React 19.3 React Native 0.88 requires `react@^19.3.0`, but the `react` peer range ended at 19.2, causing npm `ERESOLVE` in `init` and generated apps. Also make `set-react-version` fail when the React version required by React Native falls outside the `react` peer range. --- packages/app/package.json | 2 +- .../scripts/internal/set-react-version.mts | 29 +++++++++++++++++-- yarn.lock | 2 +- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 7272df5c3..fad6d4b79 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -117,7 +117,7 @@ "peerDependencies": { "@callstack/react-native-visionos": "0.76 - 0.79", "@expo/config-plugins": ">=5.0", - "react": "18.2 - 19.2", + "react": "18.2 - 19.3", "react-native": "0.76 - 0.88 || >=0.89.0-0 <0.89.0", "react-native-macos": "^0.0.0-0 || 0.76 - 0.81", "react-native-windows": "^0.0.0-0 || 0.76 - 0.83" diff --git a/packages/app/scripts/internal/set-react-version.mts b/packages/app/scripts/internal/set-react-version.mts index 458d500e5..53c2ff973 100644 --- a/packages/app/scripts/internal/set-react-version.mts +++ b/packages/app/scripts/internal/set-react-version.mts @@ -6,7 +6,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as util from "node:util"; import manifest from "../../package.json" with { type: "json" }; -import { isMain, readJSONFile } from "../helpers.js"; +import { isMain, readJSONFile, toVersionNumber, v } from "../helpers.js"; import type { Manifest } from "../types.ts"; import { writeJSONFile } from "../utils/filesystem.mjs"; import { fetchPackageMetadata, npmRegistryBaseURL } from "../utils/npm.mjs"; @@ -152,6 +152,26 @@ function fetchReactNativeWindowsCanaryInfoViaNuGet(): Promise { .then((version) => fetchPackageInfo("react-native-windows", version)); } +/** + * Throws if the React version required by React Native is not covered by our + * `react` peer range. + */ +function assertReactPeerRange(reactVersion: string): void { + const range = manifest.peerDependencies["react"]; + const m = range.match(/ - (\d+)\.(\d+)$/); + if (!m) { + throw new Error(`Unexpected 'react' peer range: ${range}`); + } + + const [, maxMajor, maxMinor] = m.map(Number); + if (toVersionNumber(reactVersion) >= v(maxMajor, maxMinor + 1, 0)) { + throw new Error( + `react@${reactVersion} is not covered by the 'react' peer range ` + + `(${range}); please update 'peerDependencies' in 'package.json'` + ); + } +} + /** * Returns an object with common dependencies. */ @@ -189,14 +209,17 @@ async function resolveCommonDependencies( const rncliIOS = devDependencies["@react-native-community/cli-platform-ios"] ?? rncli; + // Replace range to avoid React version mismatch + const react = peerDependencies["react"].replace(/^\^/, ""); + assertReactPeerRange(react); + return { "@react-native-community/cli": rncli, "@react-native-community/cli-platform-android": rncliAndroid, "@react-native-community/cli-platform-ios": rncliIOS, "@react-native/babel-preset": rnBabelPresetVersion, "@react-native/metro-config": rnMetroConfigVersion, - // Replace range to avoid React version mismatch - react: peerDependencies["react"].replace(/^\^/, ""), + react, }; } diff --git a/yarn.lock b/yarn.lock index 59380e5af..64864f9d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12991,7 +12991,7 @@ __metadata: peerDependencies: "@callstack/react-native-visionos": 0.76 - 0.79 "@expo/config-plugins": ">=5.0" - react: 18.2 - 19.2 + react: 18.2 - 19.3 react-native: 0.76 - 0.88 || >=0.89.0-0 <0.89.0 react-native-macos: ^0.0.0-0 || 0.76 - 0.81 react-native-windows: ^0.0.0-0 || 0.76 - 0.83