>
E-NO
Expo upgrade 7 Min Read

A Practical Guide to Expo Upgrades and Migrations

calendar_today Published: 2026-08-30
update Last Updated: 2026-08-30
analytics SEO Efficiency: 100%
Technical guide illustration for A Practical Guide to Expo Upgrades and Migrations.

Intro

Upgrading an Expo project is more than bumping a version number in package.json. It is a coordinated change across the Expo SDK, React Native, native projects, and often your CI/CD pipeline. Without a clear process, you risk broken builds, incompatible native modules, and unexpected runtime failures. This guide provides a practical, command-driven approach to Expo upgrades and migrations, designed for developers, DevOps consultants, and technical startup teams.

We will cover:

  • How to inventory your current Expo and React Native environment.
  • The safest path to perform an upgrade, including preflight checks and dependency alignment.
  • Verification and diagnostics to confirm the upgrade worked.
  • Common failure modes and how to recover from them.
  • A concise operations checklist you can reuse for future upgrades.

Throughout, we emphasize operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify the result, and document recovery steps before you need them. You will find concrete commands with expected outputs, failure signals, and rollback paths.

Version and Environment Inventory

Before touching any code, build a complete picture of your current setup. The goal is to know exactly what you have, what is supported, and what the upgrade target requires. This reduces surprises and gives you a baseline for rollback.

Identify Current Versions

Run these commands in your project root (the directory containing package.json):

npx expo --version

Expected output (example for SDK 46):

4.12.0
node -v

Expected output (must meet Expo's minimum, e.g., v16.13.0 for SDK 46):

v16.13.0
npx react-native --version

Expected output:

0.69.6

Check the exact Expo SDK version in your package.json:

cat package.json | grep '"expo"'

Example line:

"expo": "~46.0.0"

Also inspect all React Native related dependencies:

npm list react react-native expo

Expected output (truncated):

[email protected] /path/to/project
├── [email protected]
├── [email protected]
└── [email protected]

Capture Deployment Topology

Know where your app runs. Are you using Expo Go, a development build, or a standalone binary? This affects upgrade strategy and testing.

Check your app.json or app.config.js for expo.ios and expo.android settings, and note if you are using EAS Build or local builds. For example, a typical app.json snippet:

{
  "expo": {
    "name": "MyApp",
    "slug": "myapp",
    "version": "1.0.0",
    "ios": {
      "bundleIdentifier": "com.example.myapp"
    },
    "android": {
      "package": "com.example.myapp"
    }
  }
}

If you use EAS, run:

eas build:list --platform all --limit 5

This lists recent builds and their Expo SDK versions, helping you ensure all builds use the same version before upgrading.

Check Compatibility with Target SDK

Before upgrading, consult the official Expo upgrade guide for your target SDK. For example, if moving from SDK 46 to 47, check the Expo SDK 47 changelog for breaking changes and required React Native version. Note any changes to core packages like expo-av, expo-camera, or expo-notifications, as their APIs may have changed.

Document the current state in a simple table for clarity:

ComponentCurrent VersionTarget VersionCompatible?
Expo SDK46.0.047.0.0Yes
React Native0.69.60.70.5Yes
Node.js16.13.0>=16.13.0Yes
expo-camera~12.0.0~13.0.0Needs API review

This inventory becomes your rollback reference. Save a copy of package.json and, if applicable, your native folders (ios/ and android/) in version control before making changes.

Safe Configuration Path

The safest upgrade path uses Expo's official tools and follows a controlled sequence. We will use expo upgrade to align dependencies, then manually adjust configuration if needed.

Prerequisites and Preflight Checks

Before running the upgrade, ensure your working directory is clean and all changes are committed:

git status --porcelain

Expected output (empty if clean):

If changes exist, commit or stash them. Then create a backup branch:

git checkout -b upgrade/sdk-47

Verify that you have a recent backup of your project, especially if you are not using version control. A simple copy command:

cp -R /path/to/project /path/to/project-backup-sdk46

Note: Never store real credentials, tokens, or private keys in your repository. Use environment variables or .env files that are excluded from version control.

Run the Expo Upgrade

Use the official upgrade command:

npx expo upgrade

This command will:

  1. Determine the latest compatible SDK version.
  2. Update package.json dependencies to the recommended versions.
  3. Install the new dependencies with npm or yarn (depending on your lock file).
  4. Run any necessary post-install scripts.

Expected output (example excerpt):

? You are currently using SDK 46.0.0. Would you like to upgrade to SDK 47.0.0? Yes
Updating packages...
- expo: 46.0.0 -> 47.0.0
- react-native: 0.69.6 -> 0.70.5
- expo-camera: 12.0.0 -> 13.0.0
...
Installing dependencies...
added 25 packages, removed 12 packages, changed 40 packages

If you need to upgrade to a specific SDK version, use:

npx expo upgrade 47.0.0

After the automatic upgrade, inspect the changes in package.json with:

git diff package.json

Verify that all versions match the target SDK's expected versions. For SDK 47, the expected key versions are:

  • Expo: ~47.0.0
  • React Native: 0.70.5
  • React: 18.1.0

Manual Configuration Adjustments

Some upgrades require manual changes to configuration files. For example, in SDK 47, the expo-splash-screen configuration moved to a plugin. You may need to update app.json as follows:

Before (SDK 46):

{
  "expo": {
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    }
  }
}

After (SDK 47):

{
  "expo": {
    "plugins": [
      [
        "expo-splash-screen",
        {
          "image": "./assets/splash.png",
          "resizeMode": "contain",
          "backgroundColor": "#ffffff"
        }
      ]
    ]
  }
}

Check the upgrade guide for any breaking changes that require your attention. For instance, if you use expo-av, note that in SDK 47, the Video component's useNativeControls prop was deprecated in favor of useNativeControls inside videoStyle. Update your code accordingly.

Blast Radius Limitation

To limit the impact of the upgrade, consider doing it in stages if you have a large project. For example, first upgrade only the core Expo packages and React Native, then separately upgrade dependent libraries like expo-camera or expo-notifications. This makes it easier to isolate issues.

After any change, run the verification steps in the next section before moving on.

Verification and Diagnostics

After the upgrade, you must verify that the app builds and runs correctly. This involves static checks, bundling, and possibly running the app on a simulator or device.

Static Checks

TypeScript Check

If your project uses TypeScript, run:

npx tsc --noEmit

Expected output (if no errors):

If errors appear, address them. For example, a common error after upgrading to SDK 47 with expo-camera:

Type 'Camera' is missing the following properties from type 'CameraType': getSupportedRatiosAsync, ...

This indicates that the library API changed, and you need to update your usage.

Lint Check

Run your linter (e.g., ESLint):

npx eslint .

Expected output:

If warnings, prioritize those related to new deprecations or changed APIs.

Bundle Check

Generate a production bundle to catch module resolution errors:

npx expo export --platform all

Expected output:

Exporting iOS bundle...
Exporting Android bundle...
Export successful

If the export fails, the error message will point to the problematic module. For example:

Unable to resolve module 'some-package' from 'src/App.tsx'

This usually means a package is missing or incompatible with the new SDK.

Runtime Verification

If possible, run the app using Expo Go or a development build on a simulator/emulator.

Start the dev server:

npx expo start

Then press i for iOS simulator or a for Android emulator. Observe the app launching. Test key features, especially those using upgraded libraries.

For a more thorough check, use EAS Build to create a development build and test on a real device.

Diagnostics Commands

If the app fails to start, gather diagnostics:

npx expo-doctor

Expected output (healthy):

✔ Check Expo config
✔ Check package.json
✔ Check dependencies
✔ Check for common project issues
All checks passed

If issues found, expo-doctor provides suggestions. For example, if there are duplicate dependencies:

✖ Check dependencies
  Found duplicated packages: react-native, react
  Run `npx expo install --fix` to resolve.

Run the fix command:

npx expo install --fix

Check native logs if using a development build:

npx react-native log-ios

or

npx react-native log-android

Look for errors related to native module registration or missing modules.

Failure Modes and Recovery

Even with careful planning, upgrades can fail. Here are common failure modes, their symptoms, and recovery steps.

Dependency Version Conflicts

Symptom: npm install or yarn install fails with peer dependency errors.

Example error:

npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react-native
npm ERR!   react-native@"0.70.5" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react-native@"^0.69.0" from [email protected]

Recovery:

  • Ensure all Expo-related packages are updated to versions compatible with the new SDK. Use npx expo install to get recommended versions.
npx expo install expo-camera
  • If necessary, manually adjust package.json and run npm install with --force or --legacy-peer-deps as a temporary measure, then resolve conflicts properly.

Native Module Incompatibility

Symptom: App builds but crashes at runtime, often with a red box error like:

Invariant Violation: Native module cannot be null.

or

TypeError: null is not an object (evaluating 'NativeModules.SomeModule')

Recovery:

  • Identify the module causing the issue (e.g., ExpoCamera).
  • Check if the module's native code is included in the build. For development builds, ensure you have rebuilt after adding the package.
expo run:android

or

expo run:ios
  • If using a custom development client, rebuild it with the new modules.

Bundling Errors

Symptom: Metro bundler fails with "Unable to resolve module" or syntax errors.

Example:

Unable to resolve module 'react-native-gesture-handler' from 'src/navigation/AppNavigator.tsx'

Recovery:

  • Install the missing package with npx expo install react-native-gesture-handler.
  • If the module exists but is incompatible, clear Metro cache:
npx expo start --clear
  • If the error references a transpilation issue (e.g., unexpected token import), check your babel.config.js. Ensure it includes babel-preset-expo and any necessary plugins.

Configuration Drift

Symptom: App behavior changes after upgrade, such as splash screen not showing or permissions not requested.

Recovery:

  • Review the Expo SDK changelog for changes to configuration plugins. For example, in SDK 47, several modules now require explicit plugin configuration in app.json.
  • Compare your app.json with the default configuration generated by npx expo prebuild (if using CNG) or refer to the documentation.
  • Re-run npx expo prebuild --clean to regenerate native projects with current configuration. Be cautious as this will overwrite native changes.

Rollback Strategy

If the upgrade causes unresolvable issues, roll back to the previous version.

  1. Stop any running processes.
  2. If you created a backup branch, switch back:
git checkout main
  1. Restore the original package.json and native folders from backup:
git checkout main -- package.json ios android
  1. Reinstall dependencies:
rm -rf node_modules
npm install
  1. Verify with npx expo-doctor and a bundle check.

If you used a full project backup (folder copy), simply replace the project folder with the backup.

Operations Checklist

Use this checklist for every Expo upgrade to ensure consistency and reduce risk. Each item includes a representative example with owner and expected outcome.

Pre-Upgrade

  • [ ] Backup current state: Create a git branch and/or folder backup. (Owner: Developer, e.g., Alex Chen, git checkout -b backup/sdk46)
  • [ ] Record current versions: Run npx expo --version, node -v, npx react-native --version. Store output in a document. (Owner: DevOps, e.g., Priya Shah)
  • [ ] Review target SDK changelog: Note breaking changes and required updates. (Owner: Tech Lead, e.g., Jordan Lee)
  • [ ] Check CI/CD configuration: Ensure build pipeline is compatible with new versions. Update if necessary. (Owner: DevOps)
  • [ ] Verify clean working directory: git status must show no uncommitted changes. (Owner: Developer)

Upgrade Execution

  • [ ] Run upgrade command: npx expo upgrade (or specific version). (Owner: Developer)
  • [ ] Resolve dependency conflicts: Use npx expo install --fix if needed. (Owner: Developer)
  • [ ] Apply manual configuration changes: Update app.json, babel.config.js, etc., per changelog. (Owner: Developer)
  • [ ] Update code for breaking API changes: Adjust component usage. (Owner: Developer)

Verification

  • [ ] Run static checks: npx tsc --noEmit, npx eslint .. Ensure zero errors. (Owner: Developer)
  • [ ] Export bundle: npx expo export --platform all. Confirm success. (Owner: Developer)
  • [ ] Run on simulator/device: Test critical features. (Owner: QA, e.g., Sam Rodriguez)
  • [ ] Run npx expo-doctor: Confirm all checks pass. (Owner: Developer)
  • [ ] Performance smoke test: Compare app startup time and memory usage before/after upgrade. Record metrics. (Owner: QA)

Post-Upgrade

  • [ ] Update documentation: Note new versions and any configuration changes in README. (Owner: Tech Writer, e.g., Morgan Smith)
  • [ ] Commit changes: Create a meaningful commit message, e.g., "Upgrade to Expo SDK 47". (Owner: Developer)
  • [ ] Tag release: Tag the commit for easy rollback reference, e.g., git tag sdk-47. (Owner: Release Manager)
  • [ ] Monitor production: Observe error rates and crashes via your analytics. (Owner: SRE, e.g., Taylor Brown)

Conclusion

Expo upgrades are manageable when you follow a structured process: inventory your environment, use the official upgrade tool, verify thoroughly, and know your rollback options. This guide has provided concrete commands, expected outputs, and recovery paths to help you navigate the process with confidence.

The key takeaways are:

  • Observe before changing: Always capture current state and versions.
  • Limit blast radius: Use version control, branches, and staged upgrades.
  • Verify with real commands: Use expo-doctor, TypeScript checks, bundle exports, and runtime tests.
  • Document recovery: Have a tested rollback plan.

As a next step, choose a low-risk verification for your current Expo project. Run the inventory commands and record the results. Then, when you are ready to upgrade, follow the checklist and adapt the examples to your specific dependencies. For further reading, consult the official Expo upgrade documentation and the changelog for your target SDK.

Remember: a reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision.

Related Research

Article Quality Score

Reader usefulness 100%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL