Skip to content

feat!: bump Expo SDK 52 -> 55 (requires Reanimated 4)#161

Merged
GSTJ merged 11 commits into
mainfrom
renovate-sweep
May 19, 2026
Merged

feat!: bump Expo SDK 52 -> 55 (requires Reanimated 4)#161
GSTJ merged 11 commits into
mainfrom
renovate-sweep

Conversation

@GSTJ
Copy link
Copy Markdown
Owner

@GSTJ GSTJ commented May 18, 2026

Summary

Coordinated Expo SDK 52 → 55 upgrade for the kitchen-sink example, with the library's peer dependency floors raised to match Reanimated 4's ecosystem requirements. Replaces 10 stalled Renovate PRs (#128, #129, #130, #132, #134, #148, #150, #151, #152, #154) that were each blocked by SDK 52's pinned versions.

⚠️ Breaking change (library)

Library minimum peer versions are now:

  • react >=18.0.0
  • react-native >=0.81.0
  • react-native-gesture-handler >=2.20.0
  • react-native-reanimated >=4.0.0
  • react-native-worklets >=0.5.0 (optional; ships with Reanimated 4)

The deprecated runOnJS from react-native-reanimated was replaced with scheduleOnRN from react-native-worklets, which only exists in Reanimated 4. Consumers on Reanimated 3 or earlier must upgrade.

packages/modal/CHANGELOG.md has an Unreleased entry under "⚠ BREAKING CHANGES" with the same list.

What changed

examples/kitchen-sink (playground)

  • expo ^52.0.16^55.0.24
  • react / react-dom 18.3.119.2.0
  • react-native 0.76.90.83.6
  • expo-constants, expo-linking, expo-router, expo-splash-screen, expo-status-bar → SDK 55 line
  • react-native-gesture-handler 2.20.2~2.30.0
  • react-native-reanimated ~3.16.54.3.1
  • react-native-safe-area-context 4.12.0~5.6.2
  • react-native-screens ~4.4.0~4.23.0
  • react-native-web ~0.19.13^0.21.0
  • New: @expo/metro-runtime ~55.0.11 direct dep + pnpm.overrides pin (was stuck on 4.0.0 via stale resolution, causing getDevServer is not a function at JS bootstrap)
  • New: expo.install.exclude for react-native-reanimated (Expo SDK 55 advises 4.2.1 but its bundled compatibility.json rejects worklets 0.8.x; 4.3.1 is the authoritative fix)
  • Drop newArchEnabled from app.config.ts (default in SDK 55)
  • Fix metro.config.js for SDK 55's tightened Readonly Metro config types
  • New Expo config plugin plugins/withExpoModulesCoreSwiftStrictConcurrency.js: lowers SWIFT_STRICT_CONCURRENCY=targeted for the ExpoModulesCore pod only, working around fix(expo-modules-core): Swift 6 / Xcode 16 strict concurrency compliance expo/expo#44141 (Xcode 16 Swift 6 strict mode)

packages/modal (library)

  • Replace runOnJS with scheduleOnRN from react-native-worklets
  • Add react-native-worklets as optional peer
  • Raise peer floors (see Breaking change above)
  • devDeps bumped to match the workspace (@types/react 19, react-native 0.83.6, jest-expo 55, react-test-renderer 19, @testing-library/react-native 13.3)
  • Exclude dist from tsconfig.json (fixes typecheck/build race with bunchee)

Tooling

  • Pin metro@^0.83.3 via pnpm.overrides (was pulling 0.84.4 from @react-native/community-cli-plugin, failing expo-doctor)
  • ESLint shared config: drop parserOptions.project (superseded by projectService in typescript-eslint v8; fixes lint parse errors)
  • Drop ScrollView import from react-native-gesture-handler in kitchen-sink (use core RN to satisfy no-restricted-syntax)

CI

  • New workflow .github/workflows/e2e-ios.yml: macos-15 runner, full prebuild + Release iOS build + Maestro smoke flows, uploads Maestro debug output as artifact on failure
  • New examples/kitchen-sink/.maestro/:
    • smoke-launch.yaml and smoke-modal-open-close.yaml (run on CI; hardened with extendedWaitUntil + waitForAnimationToEnd for iOS 26 accessibility timing)
    • The existing crash-test-dropdown.yaml, issue155-crash-test.yaml, stress-test-crash.yaml are brought forward for local use (they reference screens that only exist on the fix/issue-155-navigation-crash branch, so not in CI matrix)
    • README.md documenting which flow runs where

Test plan

  • pnpm install clean (no node_modules patches required)
  • pnpm --filter @magic/kitchen-sink doctor → 15/15
  • pnpm typecheck → 4/4
  • pnpm lint → 3/3
  • pnpm --filter react-native-magic-modal build → clean
  • Local: expo prebuild --platform ios --clean + expo run:ios on iPhone 17 Pro (iOS 26.5) → BUILD SUCCEEDED, kitchen-sink home screen renders with all 9 modal buttons (screenshot verified)
  • CI: 🛠️ Branch Checkup green (in progress)
  • CI: 📱 E2E iOS (Maestro) green (in progress)
  • Manual modal interaction smoke (iOS 26 LogBox warnings still need to be investigated post-merge; UI renders, but the in-app debugger pill is not expandable in this env)
  • Android build smoke test

GSTJ added 11 commits May 18, 2026 20:47
Coordinated upgrade replacing 10 individual Renovate PRs that were
each blocked by Expo SDK 52's pinned versions.

kitchen-sink (examples):
- expo ^52.0.16 -> ^55.0.24
- react 18.3.1 -> 19.2.0
- react-dom 18.3.1 -> 19.2.0
- react-native 0.76.9 -> 0.83.6
- expo-constants, expo-linking, expo-router, expo-splash-screen,
  expo-status-bar -> SDK 55 line
- react-native-gesture-handler 2.20.2 -> ~2.30.0
- react-native-reanimated ~3.16.5 -> 4.3.1
- react-native-safe-area-context 4.12.0 -> ~5.6.2
- react-native-screens ~4.4.0 -> ~4.23.0
- react-native-web ~0.19.13 -> ^0.21.0

modal (library devDeps only; peers stay `*`):
- @types/react, react-native, jest-expo, react-test-renderer,
  @testing-library/react-native bumped to match workspace

Other:
- Drop `newArchEnabled` from app.config.ts (default in SDK 55)
- Adjust metro.config.js for tightened Metro `Readonly` config types
- Exclude `dist` from packages/modal tsconfig (fixes typecheck/build race)
- Pin reanimated to 4.3.1 (Expo SDK 55 advises 4.2.1 but its
  compatibility.json rejects worklets 0.8.x, breaking pod install).
  Added expo.install.exclude for reanimated to silence doctor.

Verified: expo-doctor 15/15, typecheck 4/4, modal builds clean,
iOS xcodebuild succeeds, app launches in iPhone 17 Pro sim.
Adds end-to-end coverage so dep bumps and SDK upgrades like this one
catch runtime breakage that typecheck/lint/expo-doctor miss.

- examples/kitchen-sink/.maestro/smoke-launch.yaml: launch + assert home
- examples/kitchen-sink/.maestro/smoke-modal-open-close.yaml:
  open primary modal, dismiss via Close Modal, assert return
- .github/workflows/e2e-ios.yml: macos-14 runner, prebuild + Release
  build + Maestro CLI; uploads debug output as artifact on failure
- Brings forward the user's crash-test/issue155/stress-test flows
  for local use; NOT run on CI (they require the issue-155 fix branch)
- .maestro/README.md documents which flows run where
@react-native/metro-config@0.85.3 (transitive of community-cli-plugin)
was pulling metro@0.84.4 on fresh installs, failing expo-doctor's
native-module compatibility check (SDK 55 ships @expo/metro requiring
metro@^0.83.3).

Local installs masked this via stale node_modules; CI caught it.
typescript-eslint v8 errors when both options are set. Also moves the
kitchen-sink ScrollView import to react-native (gesture-handler
ScrollView was flagged by no-restricted-syntax), and applies prettier
to the new .maestro/README.md.
Reanimated 4 deprecates `runOnJS` in favor of `scheduleOnRN` from
`react-native-worklets`. ESLint v4 flags the deprecation as an error.

Declares react-native-worklets as an optional peer of the modal
library since it ships transitively with Reanimated 4 already.
Two SDK 55 blockers found via runtime + CI testing:

1. pnpm resolved @expo/metro-runtime to 4.0.0 (the SDK 52 version)
   even though expo-router@55 requires ^55.0.11. The stale 4.0.0
   ships a broken messageSocket.native.ts that calls
   `getDevServer()` directly on a CJS module namespace, throwing
   "TypeError: getDevServer is not a function (it is Object)" at
   JS bootstrap. Adding @expo/metro-runtime as a direct dep of the
   kitchen-sink (and pinning it via pnpm.overrides) forces 55.0.11,
   which removes the broken file entirely.

2. expo-modules-core 55.0.25 doesn't compile under Xcode 16's
   Swift 6 complete-mode strict concurrency (MainActor/Sendable
   errors). Upstream fix (PR #44141) is unmerged. Adds an Expo
   config plugin that injects a post_integrate hook lowering
   SWIFT_STRICT_CONCURRENCY to `targeted` only for the
   ExpoModulesCore pod.

Verified: expo-doctor 15/15, typecheck 4/4, prebuild injects the
hook, pod install succeeds.
- Add JSDoc ConfigPlugin type so the JS plugin passes tsc strict mode
- Use extendedWaitUntil + waitForAnimationToEnd in smoke flows to give
  iOS 26.5's reworked accessibility tree time to settle before maestro
  queries it (iOS 26 changed when XCUITest exposes RN Text nodes)
Sets minimum peer versions to reflect what the library actually
requires after the runOnJS -> scheduleOnRN migration. Existing
consumers on older RN/Reanimated will get an install warning
instead of a confusing runtime error.

BREAKING CHANGE: react-native-reanimated >=4.0.0 is now required.
Other peers bumped to the versions Reanimated 4 itself requires:
- react >=18.0.0
- react-native >=0.81.0
- react-native-gesture-handler >=2.20.0
- react-native-worklets >=0.5.0 (optional, ships with Reanimated 4)
@GSTJ GSTJ changed the title chore: bump Expo SDK 52 -> 55 feat!: bump Expo SDK 52 -> 55 (requires Reanimated 4) May 19, 2026
@GSTJ GSTJ merged commit eaf8aec into main May 19, 2026
1 of 3 checks passed
GSTJ added a commit that referenced this pull request May 19, 2026
## Summary

Three post-merge regressions introduced by #161:

1. **`🚀 Publish` failed** — `turbo release` fanned out to
`packages/eslint-config` which tried to npm-publish without auth.
Result: `react-native-magic-modal@7.0.0` did NOT ship. Fixed by scoping
the workspace `release` script to `--filter=react-native-magic-modal`.
2. **`📚 Docs` failed** — `pnpm run docs --force` doesn't pass through to
turbo cleanly; turbo errors with `unexpected argument '--force'`. Fixed
by adding `--` so the flag reaches turbo's command target.
3. **`📱 E2E iOS` failed** — macos-15's default Xcode is 16.4 (Swift 6.0)
but `expo-modules-core@55.x` uses `@MainActor` extension attribute
syntax that requires Swift 6.1+ (Xcode 16.5 / Xcode 26). Adds a workflow
step that selects an Xcode 26 install if present.

The Podfile config plugin from #161 still helps as a fallback (lowers
strict concurrency for the ExpoModulesCore pod), so it stays.

## Once this merges

Re-trigger the `🚀 Publish` workflow on `main` (or push an empty commit)
to ship `react-native-magic-modal@7.0.0` to npm. Doing it explicitly
after this lands so the publish goes through with the scope fix.

## Test plan
- [x] `pnpm typecheck` → 4/4
- [x] `pnpm lint` → 3/3
- [ ] `🛠️ Branch Checkup` passes
- [ ] `📚 Docs` passes
- [ ] `📱 E2E iOS (Maestro)` passes (Xcode 26 fix)
GSTJ added a commit that referenced this pull request May 19, 2026
## Summary
- Bumps `react-native-magic-modal` from `6.0.6` to `7.0.0`
- Renames the `Unreleased` heading in `CHANGELOG.md` to `7.0.0
(2026-05-19)`
- Captures the Expo SDK 52 -> 55 breaking peer-dep bumps (RN 0.83, React
19, Reanimated 4, worklets 0.5) from #161 that earlier failed publish
runs lost track of.

## Why a manual bump?
The original `feat!: bump Expo SDK 52 -> 55` lived in the squashed
history of #161, but the most recent commits on `main` are
`ci(release):` / `chore:` style, so release-it's conventional-changelog
plugin computed a `patch` (6.0.6 → 6.0.7). This PR pre-stamps `7.0.0` so
the next publish workflow run picks it up verbatim.

Intentionally **no** `feat!:` / `BREAKING CHANGE:` marker on the commit
— that would cause release-it to compound the bump again.

## Test plan
- [x] `package.json` version reads `7.0.0`
- [x] `CHANGELOG.md` top section is `## 7.0.0 (2026-05-19)` with the
BREAKING CHANGES intact
- [ ] Branch checks pass
- [ ] After merge, Publish workflow ships `7.0.0` (gated on `NPM_TOKEN`
rotation — see follow-up issue)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant