skip to content

Upgrade Compatibility

Each Expo SDK pins one React Native version and a matching set of package versions, which npx expo install enforces. Interviewers ask how to upgrade safely and why skipping SDKs goes wrong.

part ofExpo (React Native)overview, primer and where to startread it →
on this pageshow

explore

questions

4

In an Expo project, why should you add libraries with npx expo install rather than npm install, and which version does it choose?

level: juniorimportance: must knowfreq 60%

answer

  1. React Native is not backwards compatible
  2. a known-good version list per SDK
  3. expo/bundledNativeModules.json
  4. unknown packages pass straight through
  5. an explicit version skips the mapping

basics

~20 s

npx expo install looks the package up in the installed SDK's list of known-compatible versions and installs that version with your package manager. npm install takes the latest release, which may target a different React Native version.

solid answer

~40 s

React Native is not backwards compatible, so a library with native code usually has to match the project's exact `react-native` version. Each Expo SDK ships a map of known-good versions, `expo/bundledNativeModules.json`, which the CLI combines with a list it fetches for that SDK. `npx expo install expo-camera` looks the package up there and installs, for SDK 57, `expo-camera@~57.0.5`, using whichever package manager the lockfile indicates. A package that is not in the map is passed through unchanged, so you get what the package manager resolves, normally the latest. If you type an explicit version, or list the package in `expo.install.exclude`, the CLI installs what you asked for and says it did so. `npm install` skips all of this, and the mismatch only shows up later as a warning or a native build failure.

code

bash · 6 lines
bash
# Installs the version SDK 57 expects (expo-camera@~57.0.5)
npx expo install expo-camera

# Several packages at once; a dev dependency via the package manager
npx expo install expo-image expo-haptics
npx expo install typescript -- -D

go deeper

for a junior

Use npx expo install for libraries in an Expo project, because it installs the version matching your SDK instead of the latest.

for a middle

Explain the known-versions map, what happens for packages outside it, and how explicit versions and install.exclude override it.

for a senior

Treat pass-through installs as unverified, check native compatibility yourself, and keep overrides recorded in install.exclude.

for a principal

Make SDK-aligned installs the team default so dependency drift is prevented at the source, not debugged in builds.

## The problem npx expo install solves On the web, a newer version of a library usually still works with an older framework. **React Native is not backwards compatible** in that way: a package that contains native code is compiled against a particular `react-native` version, and a release built for a newer React Native can fail to compile, or crash at launch, on an older one. An Expo project pins one React Native version per SDK (SDK 57 ships `react-native` 0.86.3), so every native library needs the release that matches it. `npm install some-lib` knows nothing about that. It resolves the newest release allowed by the version range, often the latest, whatever React Native it targets. ## What npx expo install does `npx expo install <package>` is a drop-in replacement for your package manager's add command: 1. It reads the project's SDK version from the installed `expo` package. 2. It loads the **known versions** for that SDK: the `bundledNativeModules.json` file shipped inside `expo`, combined with a list fetched for that SDK. The fetched list wins where both have an entry, which lets fixes reach projects without a new `expo` release, and the local file is used when offline. 3. For each package in that map, it rewrites the request to the mapped range, for example `expo-camera@~57.0.5` on SDK 57. 4. It runs your package manager (`npm`, `yarn`, `pnpm` or `bun`, chosen from the lockfile or forced with `--npm` and similar flags) to install the result. Arguments after `--` go straight to the package manager, so `npx expo install typescript -- -D` adds a dev dependency. ## What it does not do | Situation | Behaviour | |---|---| | Package is in the SDK's map | installs the mapped version or range | | Package is not in the map | passed through unchanged; the package manager picks the version | | You type `[email protected]` explicitly | installs 1.2.3 and logs that you overrode the expected version | | Package is in `expo.install.exclude` | installs what you asked for and logs the exclusion | So `npx expo install` is a **best-effort** tool. For libraries outside the map, compatibility is still your job: check the library's README and React Native Directory. ## Versions that match the SDK Since SDK 55, Expo's own packages carry the SDK major as their version (`expo-router` and `expo-camera` are `~57.0.x` on SDK 57), which makes a mismatch easy to see in `package.json`. Third-party packages in the map, such as `react-native-reanimated` or `react-native-screens`, keep their own numbering, which is exactly why a lookup table is needed. ## Where the same map is used The known-versions map is not only for installs: - `npx expo start` checks installed versions against it at startup and prints the packages that "should be updated for best compatibility" with the installed `expo` version. - `npx expo install --check` and `--fix` validate and correct the whole project against it. ## When the map and a library disagree Occasionally a library's own documentation recommends a newer release than the SDK maps, usually for a bug fix. The map is the tested combination; the newer release is not. The sensible order is: 1. Try the mapped version first and see whether the bug really affects you. 2. If you need the newer release, install it explicitly and add the package to `expo.install.exclude`, so every later check shows the deviation as deliberate. 3. Revisit the exclusion at the next SDK upgrade, when the map may have caught up and the exception can be removed. ## Mistakes to avoid - Installing native libraries with plain `npm install`, then chasing the start-up warning or a native build error. - Pinning an explicit version "to be safe" and silently opting out of the SDK's mapping. - Assuming a passed-through package is compatible because the install succeeded; installation does not check native compatibility. ## A small example A pet-adoption app on SDK 57 adds the camera for profile photos. `npx expo install expo-camera` installs `~57.0.5`, the range SDK 57 expects. `npm install expo-camera` might fetch a newer major built for a later SDK, and the app would then warn at start-up or fail during the native build.

  • npx expo install installs a community library at its latest version; is that a compatibility guarantee?
    No. Only packages in the SDK's known-versions map are pinned; anything else is passed to the package manager unchanged. For those, check the library's README and React Native Directory for React Native and New Architecture support, and watch for start-up warnings or native build errors after installing.
  • When is an explicit version with npx expo install justified?
    When you knowingly need a different release than the SDK maps, for example a fix published after the SDK. The CLI installs it and logs the override; listing the package in `expo.install.exclude` records the decision so version checks stop flagging it.

saying these in an interview costs you the question

  • npx expo install only works for packages published by Expo
  • npm install and npx expo install always pick the same version
  • A successful install proves the library is compatible
  • npx expo install replaces the project's package manager
  • Expo packages keep their own version numbers unrelated to the SDK
open as a page

What does it mean that each Expo SDK targets one React Native version, and how does that constrain which React Native version your Expo app runs?

level: middleimportance: must knowfreq 50%

basics

~20 s

An Expo SDK release is built and tested against a single React Native version, and its packages expect that version. SDK 57 targets React Native 0.86.3, so moving to a newer React Native means moving to the SDK that targets it.

open as a page

In an Expo project, what is the difference between npx expo install --check and --fix, and how would you use each in CI?

level: middleimportance: should knowfreq 38%

basics

~10 s

npx expo install --check compares installed packages with the SDK's expected versions and reports mismatches, prompting to fix when interactive and exiting non-zero in CI. --fix installs the expected versions unconditionally.

open as a page

How would you upgrade an Expo pet-adoption app from SDK 54 to SDK 57, and why go one SDK at a time instead of jumping straight there?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Upgrade in three steps, 54 to 55, 55 to 56 and 56 to 57: bump expo, run npx expo install --fix, regenerate native folders, follow that SDK's release notes and test. Each step then breaks in a small, attributable way.

open as a page