@nx/expo - Migrations
For an overview of the plugin and setup instructions, see the @nx/expo introduction.
The @nx/expo plugin provides various migrations to help you migrate to newer versions of expo projects within your Nx workspace. Below is a complete reference for all available migrations.
24.0.x
Section titled “24.0.x”remove-deprecated-executors
Section titled “remove-deprecated-executors”Version: 24.0.0-beta.3
Converts targets using the removed @nx/expo executors to inferred tasks; an AI prompt finishes the targets the converter cannot convert safely and validates the project graph.
Convert @nx/expo targets to inferred tasks
Section titled “Convert @nx/expo targets to inferred tasks”Nx 24 removes the @nx/expo:build, @nx/expo:export, @nx/expo:install, @nx/expo:prebuild, @nx/expo:run, @nx/expo:serve, @nx/expo:start and @nx/expo:submit executors and their @nrwl aliases. The migration registers @nx/expo/plugin in nx.json and converts the targets that use those executors to inferred tasks. Executor options become command-line arguments of the inferred tasks. A target with nothing left to override is removed from project configuration. When several projects share the same remaining options, those options move to a targetDefaults entry scoped to the plugin. targetDefaults entries keyed by a removed executor are removed once no target uses that executor.
The migration lists the targets the converter cannot convert safely. In agentic runs, an AI prompt finishes them and validates the project graph.
The @nx/expo:build-list, @nx/expo:sync-deps, @nx/expo:update and @nx/expo:ensure-symlink executors stay.
The inferred install and prebuild tasks run expo install and expo prebuild as they are. @nx/expo:install installed packages into the root package.json and then synced them into the app’s. The inferred install runs in the app directory, so packages go into the app’s package.json and no longer into the root one. To keep dependencies in the root package.json, install them there and sync them with the kept @nx/expo:sync-deps executor. @nx/expo:prebuild ran expo prebuild --no-install and then installed packages and CocoaPods itself, which expo prebuild does by default.
@nx/expo:build copied the root dependencies and lock file into the app before eas build. The add-eas-build-pre-install migration moves that step to an eas-build-pre-install script that EAS Build runs.
Sample code changes
Section titled “Sample code changes”The install target has no options that differ from the inferred target, so it is removed. The export options become CLI arguments.
Before
Section titled “Before”{ "name": "demo", "targets": { "export": { "executor": "@nx/expo:export", "options": { "platform": "all", "outputDir": "dist/demo" } }, "install": { "executor": "@nx/expo:install", "options": {} } }}{ "name": "demo", "targets": { "export": { "options": { "args": ["--output-dir=../../dist/demo", "--platform=all"] } } }}{ "plugins": [ { "plugin": "@nx/expo/plugin", "options": { "buildTargetName": "build", "exportTargetName": "export", "installTargetName": "install", "prebuildTargetName": "prebuild", "runAndroidTargetName": "run-android", "runIosTargetName": "run-ios", "serveTargetName": "serve", "startTargetName": "start", "submitTargetName": "submit" } } ]}add-eas-build-pre-install
Section titled “add-eas-build-pre-install”Version: 24.0.0-beta.3
Adds an eas-build-pre-install script to Expo apps with an eas.json, which copies the root dependencies and lock file into the app on EAS Build as the removed @nx/expo:build executor did locally.
Add the EAS Build pre-install script to Expo apps
Section titled “Add the EAS Build pre-install script to Expo apps”Nx 24 removes the @nx/expo:build executor. In a workspace without package manager workspaces, that executor copied the root dependencies, devDependencies, overrides (or resolutions) and lock file into the app before running eas build, then restored the app’s files. Without that step, EAS installs the app’s "*" dependencies at whatever versions are latest.
The migration adds an eas-build-pre-install script to the package.json of every project with an eas.json, and writes tools/scripts/eas-build-pre-install.mjs. EAS Build runs the script on the build machine before installing dependencies, and it does the same copy there, so your own files are never modified. It also copies the root pnpm settings, and with Yarn it gives the app the root package name, because EAS installs with a frozen lock file and both package managers check those against it. A workspace with pnpm patchedDependencies still fails to install, because the patch paths resolve from the app directory. With package manager workspaces enabled, the script exits without changes.
A project that already defines eas-build-pre-install keeps its script and is listed in the migration’s next steps, so you can add the new command to it.
Sample code changes
Section titled “Sample code changes”Before
Section titled “Before”{ "name": "mobile", "scripts": { "eas-build-post-install": "cd ../../ && node tools/scripts/eas-build-post-install.mjs . apps/mobile" }}{ "name": "mobile", "scripts": { "eas-build-post-install": "cd ../../ && node tools/scripts/eas-build-post-install.mjs . apps/mobile", "eas-build-pre-install": "cd ../../ && node tools/scripts/eas-build-pre-install.mjs . apps/mobile" }}23.1.x
Section titled “23.1.x”update-23-1-0-update-metro-config-for-expo-56
Section titled “update-23-1-0-update-metro-config-for-expo-56”Version: 23.1.0-beta.1
Update generated metro.config.js to use @expo/metro for Expo SDK 56
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 56.0.0 |
update-23-1-0-replace-standalone-metro-for-expo-56
Section titled “update-23-1-0-replace-standalone-metro-for-expo-56”Version: 23.1.0-beta.1
Replace standalone metro-config/metro-resolver with @expo/metro for Expo SDK 56
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 56.0.0 |
update-23-1-0-update-jest-winter-runtime-for-expo-56
Section titled “update-23-1-0-update-jest-winter-runtime-for-expo-56”Version: 23.1.0-beta.1
Update Jest test-setup for the Expo SDK 56 winter runtime
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 56.0.0 |
23.1.0-package-updates
Section titled “23.1.0-package-updates”Version: 23.1.0-beta.1
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
expo | ~56.0.0 | Updated only |
expo-splash-screen | ~56.0.10 | Updated only |
expo-status-bar | ~56.0.4 | Updated only |
expo-system-ui | ~56.0.5 | Updated only |
@expo/cli | ~56.1.14 | Updated only |
babel-preset-expo | ~56.0.14 | Updated only |
react | ^19.2.0 | Updated only |
react-dom | ^19.2.0 | Updated only |
@types/react | ^19.2.0 | Updated only |
react-native | 0.85.3 | Updated only |
react-native-web | ~0.21.0 | Updated only |
@expo/metro | ~56.0.0 | Updated only |
@expo/metro-config | ~56.0.13 | Updated only |
@expo/metro-runtime | ~56.0.14 | Updated only |
@expo/vector-icons | ~15.1.0 | Updated only |
react-native-svg-transformer | ~1.5.1 | Updated only |
react-native-svg | 15.12.1 | Updated only |
@testing-library/react-native | ~13.2.0 | Updated only |
jest-expo | ~56.0.4 | Updated only |
@babel/runtime | ~7.27.6 | Updated only |
23.0.x
Section titled “23.0.x”update-23-0-0-migrate-create-nodes-v2-import
Section titled “update-23-0-0-migrate-create-nodes-v2-import”Version: 23.0.0-beta.24
Rename imports of createNodesV2 from @nx/expo/plugin to the canonical createNodes export.
Rename createNodesV2 imports to createNodes
Section titled “Rename createNodesV2 imports to createNodes”@nx/expo renamed its primary inferred-plugin export from createNodesV2 to createNodes. The createNodesV2 name is preserved as a deprecated alias for now, but new code should use createNodes.
This migration scans every .ts, .tsx, .cts, and .mts file in your workspace and rewrites named imports and re-exports of createNodesV2 from @nx/expo/plugin to createNodes.
Sample Code Changes
Section titled “Sample Code Changes”Before
Section titled “Before”import { createNodesV2 } from '@nx/expo/plugin';import { createNodes } from '@nx/expo/plugin';Aliases are preserved (createNodesV2 as cn becomes createNodes as cn), and if a file already imports both names ({ createNodes, createNodesV2 }) the redundant binding is dropped.
What is not rewritten
Section titled “What is not rewritten”Only static import/export named bindings from @nx/expo/plugin are rewritten. Namespace imports, dynamic import(...), require(...) destructuring, and property access such as plugin.createNodesV2 are left untouched — they keep working through the createNodesV2 runtime alias. Update those by hand if you want to drop the deprecated name everywhere.
23.0.0-package-updates
Section titled “23.0.0-package-updates”Version: 23.0.0-beta.25
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
expo | ~55.0.26 | Updated only |
expo-splash-screen | ~55.0.21 | Updated only |
expo-status-bar | ~55.0.6 | Updated only |
expo-system-ui | ~55.0.18 | Updated only |
@expo/cli | ~55.0.11 | Updated only |
babel-preset-expo | ~55.0.7 | Updated only |
react | ^19.2.0 | Updated only |
react-dom | ^19.2.0 | Updated only |
@types/react | ^19.2.0 | Updated only |
react-native | ~0.83.6 | Updated only |
react-native-web | ~0.21.0 | Updated only |
@expo/metro-config | ~55.0.8 | Updated only |
@expo/metro-runtime | ~55.0.11 | Updated only |
react-native-svg-transformer | ~1.5.1 | Updated only |
react-native-svg | 15.12.1 | Updated only |
@testing-library/react-native | ~13.2.0 | Updated only |
jest-expo | ~55.0.18 | Updated only |
@babel/runtime | ~7.27.6 | Updated only |
metro-config | ~0.83.0 | Updated only |
metro-resolver | ~0.83.0 | Updated only |
22.2.x
Section titled “22.2.x”update-22-2-0-create-ai-instructions-for-expo-54
Section titled “update-22-2-0-create-ai-instructions-for-expo-54”Version: 22.2.0-beta.3
Create AI migration instructions for Expo SDK 54 upgrade
update-22-2-0-add-expo-system-ui
Section titled “update-22-2-0-add-expo-system-ui”Version: 22.2.0-beta.3
Add expo-system-ui dependency for Expo SDK 54
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 54.0.0 |
update-22-2-0-update-jest-for-expo-54
Section titled “update-22-2-0-update-jest-for-expo-54”Version: 22.2.0-beta.3
Update Jest configuration for Expo SDK 54
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 54.0.0 |
22.2.0-package-updates
Section titled “22.2.0-package-updates”Version: 22.2.0-beta.3
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
expo | ~54.0.0 | Updated only |
expo-splash-screen | ~31.0.11 | Updated only |
expo-status-bar | ~3.0.8 | Updated only |
expo-system-ui | ~6.0.8 | Updated only |
@expo/cli | ~54.0.16 | Updated only |
babel-preset-expo | ~54.0.7 | Updated only |
react | ^19.1.0 | Updated only |
react-dom | ^19.1.0 | Updated only |
@types/react | ^19.1.0 | Updated only |
react-native | ~0.81.5 | Updated only |
react-native-web | ~0.21.0 | Updated only |
@expo/metro-config | ~54.0.9 | Updated only |
@expo/metro-runtime | ~6.1.2 | Updated only |
react-native-svg-transformer | ~1.5.1 | Updated only |
react-native-svg | 15.12.1 | Updated only |
@testing-library/react-native | ~13.2.0 | Updated only |
jest-expo | ~54.0.13 | Updated only |
@babel/runtime | ~7.27.6 | Updated only |
metro-config | ~0.83.0 | Updated only |
metro-resolver | ~0.83.0 | Updated only |
21.4.x
Section titled “21.4.x”update-21-4-0-remove-deprecated-deps
Section titled “update-21-4-0-remove-deprecated-deps”Version: 21.4.0-beta.0
Remove deprecated dependencies from package.json
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 53.0.0 |
update-21-4-0-update-splash-screen-config
Section titled “update-21-4-0-update-splash-screen-config”Version: 21.4.0-beta.0
Update Expo splash screen configuration to use the new format
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 53.0.0 |
update-21-4-0-add-jest-resolver
Section titled “update-21-4-0-add-jest-resolver”Version: 21.4.0-beta.0
Add custom Jest resolver to handle Expo winter runtime issues
Requires
Section titled “Requires”| Name | Version |
|---|---|
expo | >= 53.0.0 < 54.0.0 |
21.4.0-package-updates
Section titled “21.4.0-package-updates”Version: 21.4.0-beta.0
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
expo | ~53.0.10 | Updated only |
expo-splash-screen | ~0.30.9 | Updated only |
expo-status-bar | ~2.2.3 | Updated only |
@expo/cli | ~0.24.14 | Updated only |
babel-preset-expo | ~13.2.0 | Updated only |
react | 19.0.0 | Updated only |
react-dom | 19.0.0 | Updated only |
@types/react | ~19.0.10 | Updated only |
react-native | ~0.79.3 | Updated only |
react-native-web | ~0.20.0 | Updated only |
@expo/metro-config | ~0.20.14 | Updated only |
@expo/metro-runtime | ~5.0.4 | Updated only |
react-native-svg-transformer | ~1.5.1 | Updated only |
react-native-svg | ~15.11.2 | Updated only |
@testing-library/react-native | ~13.2.0 | Updated only |
jest-expo | ~53.0.7 | Updated only |
@babel/runtime | ~7.27.6 | Updated only |