Migrating to v10
v10 moves the starter from pnpm to Bun and from Expo SDK 54 to SDK 57 (React Native 0.86, React 19.2). This guide lists every change that affects an existing project, in the order we recommend applying them.
Work on a new branch and commit after each step, so you can run the checks and find problems early.
Before you start
Section titled “Before you start”- Install Bun and Node.js 24 LTS. Node is still required, because Expo CLI and Metro run on it.
- For iOS builds, use Xcode 26.4 or newer. SDK 56 raised the minimum iOS version to 16.4.
- Read the official Expo changelogs for SDK 55, SDK 56 and SDK 57 if your app uses Expo modules the starter does not include.
1. Switch from pnpm to Bun
Section titled “1. Switch from pnpm to Bun”- Set
"packageManager": "bun@1.3.14"inpackage.jsonand change thepreinstallscript tonpx only-allow bun. - Replace
pnpm runwithbun runin your scripts, Husky hooks and CI workflows. The starter’s CI uses thesetup-node-bun-installcomposite action. - Delete
.npmrcand add abunfig.tomlthat keeps the flatnode_moduleslayout Metro expects:
[install]linker = "hoisted"- Add a
.nvmrcfile containing24. - Delete
pnpm-lock.yamlandnode_modules, then runbun install.
2. Upgrade to Expo SDK 57
Section titled “2. Upgrade to Expo SDK 57”bunx expo install expo@^57.0.0 --fixbunx expo install --fixRun the second command until it reports that dependencies are up to date. Expo cannot edit app.config.ts itself, so apply the config changes from step 4 by hand.
If react-native-worklets is listed in expo.install.exclude in package.json, remove it so Expo manages its version.
3. Move navigation imports to Expo Router
Section titled “3. Move navigation imports to Expo Router”Since SDK 56, expo-router no longer depends on React Navigation packages directly. Run the official codemod:
bunx expo-codemod sdk-56-expo-router-react-navigation-replace srcIt rewrites imports such as ThemeProvider, useIsFocused and NavigationContainer from @react-navigation/native to expo-router/react-navigation.
4. Update the app config
Section titled “4. Update the app config”In app.config.ts:
- Remove
newArchEnabled. SDK 55 removed the legacy architecture, so the option no longer exists. - Remove the
react-native-edge-to-edgeplugin and uninstall the package. Edge-to-edge is built in since SDK 55. - Add the
expo-imageandexpo-status-barplugins. - If you build with Xcode 27, add scene support with
expo-build-properties:
[ 'expo-build-properties', { ios: { // Required for apps built with the iOS 27 SDK (Xcode 27+). Default from SDK 58. enableSceneSupport: true, }, },],Replace SystemBars from react-native-edge-to-edge with StatusBar from expo-status-bar. See src/components/ui/focus-aware-status-bar.tsx for an example.
Because the app now always draws under the status bar, pass the safe-area inset to FlashMessage, otherwise messages are hidden behind the status bar:
const insets = useSafeAreaInsets();// ...<FlashMessage position="top" statusBarHeight={insets.top} />5. Update Babel and TypeScript config
Section titled “5. Update Babel and TypeScript config”- Remove
'react-native-reanimated/plugin'frombabel.config.js.babel-preset-expoadds the worklets plugin automatically. - TypeScript 6 deprecates
baseUrl. Remove it fromtsconfig.jsonand use explicit paths:
{ "compilerOptions": { "paths": { "@/*": ["./src/*"], "@env": ["./env.ts"] }, "types": ["jest", "node"] }}- Imports of the bare
envmodule relied onbaseUrl. Change them to@env, and point the@envalias inbabel.config.jsat./env.ts. - TypeScript 6 also type-checks side-effect imports such as
import '../global.css'. Add acss.d.tsfile containingdeclare module '*.css';. - Add
react-test-rendereras a dev dependency, pinned to your React version, for React Native Testing Library.
6. Replace react-native-restart
Section titled “6. Replace react-native-restart”Expo ships reloadAppAsync, which reloads the app in both debug and release builds. Uninstall react-native-restart and replace both RNRestart.restart() and DevSettings.reload():
import { reloadAppAsync } from 'expo';
reloadAppAsync('Language changed');7. Optional: adopt the new v10 features
Section titled “7. Optional: adopt the new v10 features”- React Compiler: run
bunx expo install babel-plugin-react-compilerand setexperiments.reactCompiler: trueinapp.config.ts. You can then removeeslint-plugin-react-compiler, becauseeslint-plugin-react-hooksv7 includes its rules. See React Compiler. - Nitro Fetch: run Axios on a native HTTP stack in release builds. See Native Networking with Nitro Fetch.
- Argent: let AI assistants drive simulators and emulators. See AI Device Control.
8. Regenerate native projects and verify
Section titled “8. Regenerate native projects and verify”rm -rf ios androidbun run prebuildbunx expo-doctor@latestbun run check-allThen build and run the app on both platforms with bun ios and bun android, and create a new development build if you use EAS.
Troubleshooting
Section titled “Troubleshooting”Cannot read property 'useCallback' of nullat runtime: two copies of React are installed. This happens when a lockfile converted from pnpm keeps old peer versions. Deletebun.lockandnode_modules, then runbun installagain.Package subpath './v4' is not defined by "exports"from ESLint:eslint-plugin-react-hooksresolved an oldzod-validation-error. Add"overrides": { "zod-validation-error": "^4.0.2" }topackage.json.- Expo Doctor reports
react-native-nitro-fetchas untested on the New Architecture: the React Native Directory metadata is outdated. Nitro modules only run on the New Architecture. Add the package toexpo.doctor.reactNativeDirectoryCheck.exclude.