← All posts

How to update an Expo app to the latest SDK 57 packages without breaking the build

A repeatable checklist for updating Expo SDK 57 apps: expo-doctor, expo install --fix, stale native folders, patch-package pitfalls, and the verification steps that catch breakage before your users do.

September 14, 2026 · Thomino

Every few weeks Expo publishes new patch releases for SDK 57, and every few weeks npx expo-doctor starts telling you that twenty packages are out of date. Updating is usually a one-liner. Sometimes it takes down your iOS build for a day.

This is the process I use to update 21 production React Native templates in a single pass. It's written so you can run it on one app. The order matters: each step catches a class of problem that the next step would otherwise hide.

1. Start from a clean tree

Commit or stash everything first. The update touches package.json, the lockfile and possibly native config, and you want the diff to contain only the update. Check that git status is clean before you start.

2. Run expo-doctor and read the whole output

npx expo-doctor

On a healthy app you get "21/21 checks passed". On an app that's behind you'll see one failure:

✖ Check that packages match versions required by installed Expo SDK
  20 packages out of date.

Read the rest of the output too. If there's a second failure, note it now. In our batch one app also failed Check Expo config for common issues, and it was a pre-existing problem that had nothing to do with versions (more on that below).

3. Let Expo pick the versions

npx expo install --fix

This bumps every Expo-managed package to the version Expo has tested against your SDK. Don't hand-edit versions and don't run npm update, which ignores Expo's compatibility table. After it finishes, run npx expo-doctor again. The package check should pass.

4. Check whether your patches survived

If your project uses patch-package, the install output will tell you whether each patch still applies:

patch-package expo-modules-jsi
  Patch file: patches/expo-modules-jsi+57.0.4.patch
  Patch was made for version: 57.0.4
  Installed version: 57.1.0
patch-package finished with 1 error(s).

A patch is tied to the exact version it was written for. When the version bumps, one of three things is true:

  • The fix landed upstream. Delete the patch, remove the postinstall script and the patch-package dev dependency if nothing else needs them, and run npm install to sync the lockfile.
  • The fix is still needed and still applies. Regenerate it against the new version with npx patch-package <package> after re-applying your change by hand.
  • The bug moved. The old fix is obsolete but a new one is required. This is what happened to us with expo-modules-jsi 57.1.0, which is covered in its own post.

Whatever you do, don't leave a failing patch in place. It prints an error on every install and hides real problems.

5. Type-check and bundle

npx tsc --noEmit
npx expo export --platform ios --output-dir /tmp/export-test

TypeScript catches API changes in the updated packages. The export forces Metro to compile the full bundle, which catches things a dev server won't until you navigate to the broken screen. Both should finish with zero errors. Together they take about a minute.

6. Boot the dev server

npx expo start

Watch the terminal for the first 20 seconds. You're looking for config warnings, plugin errors, and anything printed in yellow or red before "Waiting on http://localhost:8081". If you're scripting this, set CI=1 so Metro doesn't wait for keyboard input, and kill the process group afterwards. Killing only the npx wrapper leaves the Metro child running on its port. I learned that the hard way with 21 orphaned servers.

7. Build the native app

This is the step people skip, and it's the only one that would have caught the Xcode 26 failure. Nothing above compiles Swift or Kotlin.

npx expo prebuild --clean --platform ios
npx expo run:ios

Use --clean. A stale ios folder from a previous build carries an old Podfile.lock, and after a package update pod install will fail with "could not find compatible versions" or complain about a module you removed months ago. Regenerating the folder takes a minute and removes a whole class of false alarms. The folder is gitignored in a managed project, so nothing is lost.

If the build fails inside a specific pod, that pod's version is where to look. The error is almost always in the first ten lines of red output, not the last.

8. Fix what doctor found in step 2

Now go back to any non-version failure. The one we hit:

✖ Check Expo config for common issues
You have an app.json file in your project, but your app.config.js is not using the values from it.

The app had a dynamic config that started with const base = require('./app.json').expo. That works at runtime, but doctor only recognises the function form. Changing it to module.exports = ({ config }) => ({ ...config, /* overrides */ }) gives you the same resolved config and a green check. You can prove the output is identical by hashing npx expo config --type public --json before and after.

9. Don't touch the audit fixer's big red button

npm install will report vulnerabilities and suggest npm audit fix --force. Don't. On an Expo project that command resolves the moderate query-string findings by downgrading expo-router by a major version. Plain npm audit fix without the flag is safe. The remaining findings are usually transitive and blocked upstream.

10. Commit with the lockfile

Commit package.json, package-lock.json and any patch changes together. A lockfile that's out of sync with the manifest is the number one reason a teammate's npm ci fails the next morning.

The checklist

[ ] git status clean
[ ] npx expo-doctor            -> note every failure, not just versions
[ ] npx expo install --fix
[ ] npx expo-doctor            -> package check passes
[ ] patches/ still apply, or are updated or removed
[ ] npx tsc --noEmit           -> 0 errors
[ ] npx expo export --platform ios
[ ] npx expo start             -> no warnings in the first 20s
[ ] npx expo prebuild --clean && npx expo run:ios
[ ] fix non-version doctor findings
[ ] npm audit fix (never --force)
[ ] commit package.json + lockfile + patches together

Per app this takes ten minutes when nothing is wrong, and it tells you within ten minutes when something is. Our templates go through exactly this pass on every Expo release, which is why the version you download is the one that builds.