Upgrading a live Expo app from SDK 53 to 57 in one jump
Why it couldn't wait
Google Play started requiring targetSdk 36 for app updates on Aug 31, 2026. My gradle.properties pinned targetSdkVersion=35, which would have blocked every future update. Fixing that properly meant moving to a current Expo SDK. Since React Native 0.82, that also means the New Architecture: the old architecture no longer exists.
So this was an architecture migration with a deadline, not a routine dependency bump.
What I did
1. Jumped four SDK versions at once (53 → 57)
I did it in one pass instead of four separate upgrades, on its own branch (upgrade/expo-sdk-57), merged the next day as a single PR.
2. Migrated from the old to the New Architecture
The checked-in MainApplication.kt was still force-disabling bridgeless mode, TurboModules and Fabric. Under SDK 57 that would have broken the app.
3. Switched to Continuous Native Generation (CNG)
Instead of hand-patching old native code, I deleted the checked-in android/ and ios/ folders and let Expo Prebuild regenerate them from config. This removed the stale flags and makes future upgrades much cheaper.
What broke (or would have)
- An undeclared dependency. Three components used
@expo/vector-icons, but it was never listed inpackage.json. It worked on SDK 53 only because it came bundled withexpo. On SDK 57 it doesn't, so the build would have failed. Fix: declared it explicitly. - A duplicate export.
constants/index.jsexportedCOLORStwice. Older tooling tolerated it and the new toolchain wouldn't. Fix: removed the duplicate. - Dead weight. I removed
@react-navigation/*,styled-components,expo-av,react-native-gesture-handler,react-native-screensand unused Babel aliases, after checking that each had zero imports. Fewer packages means fewer things that can break the next upgrade.
After the merge
- Sep 4:
expo-doctorflagged six packages behind their expected patch versions. I aligned them. - Sep 21: local iOS builds broke on Xcode 26.2. Swift 6.2.3 rejected some Swift concurrency attributes in
expo-modules-jsi, and SDK 57 officially wants Xcode 26.4+. I didn't want to wait for an upstream fix or downgrade Xcode, so I patched the installed package withpatch-package, set to apply automatically onpostinstall.
Result
- The app is on the current SDK and the New Architecture, and meets Google Play's targetSdk 36 rule for updates.
- The native folders are now generated, not hand-maintained, so the next upgrade is a config change instead of a native-code excavation.
- Upgrade to production in about 4 days: ~2 days of upgrade work, 1 day of full testing, and the release to Google Play the day after.