# Reference

## Work with monorepos

Learn about setting up Expo projects in a monorepo with workspaces.

Monorepos, or _"monolithic repositories"_, are single repositories containing multiple apps or packages. They can help speed up development for larger projects, make it easier to share code, and act as a single source of truth. This guide will set up a simple monorepo with an Expo project. Expo has first-class support for monorepos managed with package managers supporting workspaces: [Bun](https://bun.sh/docs/install/workspaces), [npm](https://docs.npmjs.com/cli/using-npm/workspaces), [pnpm](https://pnpm.io/workspaces), and [Yarn](https://yarnpkg.com/features/workspaces) (v1 Classic and Berry). Expo automatically detects monorepos and configures new app projects added to a monorepo. The detection is based on the workspace configuration in your project.
> **info** Monorepos are not for every project. They're useful if multiple apps live in a single repository and share code, or can be helpful to colocate native modules with your app. The tradeoff is increased complexity when setting up and configuring tooling. Check whether your tools and libraries work well within a monorepo before setting one up.
Note: Automatic Configuration (Migrating to SDK 52+)
---
<a id="automatic-configuration" />
Since SDK 52, Expo configures Metro automatically for monorepos. You don't have to manually configure Metro when using monorepos if you use [`expo/metro-config`](/guides/customizing-metro/).
If you're migrating to an Expo SDK version after 52 and have a **metro.config.js** that manually modifies one of the following properties, delete these from your configuration:
- `watchFolders`
- `resolver.nodeModulesPath`
- `resolver.extraNodeModules`
- `resolver.disableHierarchicalLookup`
After deleting these options, you'll need to run Expo with `npx expo start --clear` once to erase the outdated Metro cache. If your app continues working as expected afterwards, it's a regular Node monorepo and won't need any special configuration going forward.
---
Note: Manual Configuration (Before SDK 52)
---
<a id="manual-configuration" />
Since SDK 52, Expo's Metro config has monorepo support for Bun, npm, pnpm and Yarn and configures itself automatically. You don't have to manually configure Metro when using monorepos if you use the config from [`expo/metro-config`](/guides/customizing-metro/). If that's the case, you don't need to manually configure monorepo support.
Before SDK 52, to configure a monorepo with Metro manually, there were two manual changes:
1. Metro had to be configured to watch code within the monorepo manually (for example, not just **apps/cool-app**.)
2. Metro's resolution had to be adjusted to find packages in other workspaces and multiple `node_modules` folders (for example, **apps/cool-app/node_modules** or **node_modules**.)
The configuration was adjusted by [creating a **metro.config.js**](/guides/customizing-metro/#customizing) with the following content:
```js metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');
// This can be replaced with `find-yarn-workspace-root`
const monorepoRoot = path.resolve(__dirname, '../..');
const config = getDefaultConfig(__dirname);
// 1. Watch all files within the monorepo
config.watchFolders = [monorepoRoot];
// 2. Let Metro know where to resolve packages and in what order
config.resolver.nodeModulesPaths = [
  path.resolve(projectRoot, 'node_modules'),
  path.resolve(monorepoRoot, 'node_modules'),
];
module.exports = config;
```
> Learn more about [customizing Metro](/guides/customizing-metro).
---
## Setting up a monorepo
In a monorepo, your app will typically be a in sub-directory of your repository and your package manager is configured to allow you to add dependencies to other packages from within your monorepo.
For example, a basic structure of a monorepo containing Expo apps may look like this:
- **apps**: Contains multiple projects, including Expo apps.
- **packages**: Contains different packages used by apps.
- **package.json**: Root package file.
All monorepos should have a "root" **package.json** file. It is the main configuration for monorepos and may contain tools installed for all projects in the repository. Depending on which package manager you're using, the steps for setting up workspaces might differ, but for [Bun](https://bun.sh/docs/install/workspaces), [npm](https://docs.npmjs.com/cli/using-npm/workspaces), and [Yarn](https://yarnpkg.com/features/workspaces), a `workspaces` property should be added to the root **package.json** file that specifies [glob patterns](https://classic.yarnpkg.com/lang/en/docs/workspaces/#toc-tips-tricks) for all workspaces in your monorepo:
```json package.json
{
  "name": "monorepo",
  "private": true,
  "version": "0.0.0",
  "workspaces": ["apps/*", "packages/*"]
}
```
For [pnpm](https://pnpm.io/workspaces), you'll have to create a [**pnpm-workspace.yaml**](https://pnpm.io/pnpm-workspace_yaml) instead:
```yaml pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
```
### Create your first app
Now that you have the basic monorepo structure set up, add your first app.
Before you create your app, you have to create the **apps** directory. This directory contains all separate apps or websites that belong to this monorepo. Inside this **apps** directory, you can create a sub-directory that contains the Expo app.
<Terminal
  cmd={{
    npm: ['$ npx create-expo-app@latest apps/cool-app'],
    yarn: ['$ yarn create expo-app apps/cool-app'],
    pnpm: ['$ pnpm create expo-app apps/cool-app'],
    bun: ['$ bun create expo apps/cool-app'],
  }}
/>
> If you have an existing app, you can copy all those files into a directory inside **apps**.
After copying or creating the first app, install your dependencies with your package manager from the root directory of your monorepo to check for common warnings.
### Create a package
Monorepos can help us group code in a single repository. That includes apps but also separate packages. They also don't need to be published. The [Expo repository](https://github.com/expo/expo) uses this as well. All the Expo SDK packages live inside the [**packages**](https://github.com/expo/expo/tree/main/packages) directory in our repo. It helps us test the code inside one of our [**apps**](https://github.com/expo/expo/tree/main/apps/native-component-list) directory before we publish them.
Let's go back to the root and create the **packages** directory. This directory can contain all the separate packages that you want to make. Once you are inside this directory, we need to add a new sub-directory. The sub-directory is a separate package that we can use inside our app. In the example below, we named it **cool-package**.
<Terminal
  cmd={{
    npm: ['$ mkdir -p packages/cool-package && cd packages/cool-package && npm init'],
    yarn: ['$ mkdir -p packages/cool-package && cd packages/cool-package && yarn init'],
    pnpm: ['$ mkdir -p packages/cool-package && cd packages/cool-package && pnpm init'],
    bun: ['$ mkdir -p packages/cool-package && cd packages/cool-package && bun init --minimal'],
  }}
/>
We won't go into too much detail in creating a package. If you are not familiar with this, consider using a simple app without monorepos. But, to make the example complete, let's add an **index.js** file with the following content:
```js index.js
export const greeting = 'Hello!';
```
### Using the package
Like standard packages, we need to add our **cool-package** as a dependency to our **cool-app**. The main difference between a standard package, and one from the monorepo, is you'll always want to use the _"current state of the package"_ instead of a version. Let's add **cool-package** to our app by adding `"cool-package": "*"` to our app **package.json** file:
```json package.json
{
  "name": "cool-app",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "cool-package": "*",
    "expo": "~54.0.0",
    "expo-status-bar": "~3.0.6",
    "react": "19.1.0",
    "react-native": "0.81.1"
  }
}
```
Bun, npm, and pnpm support specifying workspace dependencies using `"workspace:*"` instead of `"*"`. This will ensure that the workspace package never resolves a published package of the same name from the npm registry, but is optional.
After adding the package, install your dependencies with your package manager from the root directory of your monorepo to check for common warnings once again.
Now you should be able to use the package inside your app! To test this, let's edit the **App.js** in your app and render the `greeting` text from our **cool-package**.
```jsx App.js
import { greeting } from 'cool-package';
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { Text, View } from 'react-native';
export default function App() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>{greeting}</Text>
      <StatusBar style="auto" />
    </View>
  );
}
```
## Common issues
Monorepos may cause resolution and dependency issues that a regular project won't. They require more in-depth knowledge and require specific tooling configuration. You take on increased complexity and will need to solve issues you wouldn't run into without workspaces. Here are a couple of common issues you might encounter.
### Package managers with isolated dependencies
> **info** From **SDK 54**, Expo supports isolated dependencies and isolated installations.
> With **SDK 53**, disabling isolated dependencies is recommended, or you may encounter native build errors and dependency conflicts.
[Bun](https://bun.com/docs/install/isolated) and [pnpm](https://pnpm.io/settings#nodelinker) have first-class support for isolated installs. For pnpm, this is the default installation strategy unless it's disabled.
With isolated dependencies, package managers don't hoist packages from nested `node_modules` directories into higher ones. Instead, they create a central directory that contains your Node modules and create links to this directory. This dependency structure enforces that packages may only access their explicitly declared dependencies. This is a much stricter installation strategy than the traditional **hoisted** installation strategy, which are npm's and Yarn's default, to install dependencies in a flattened structure.
A side-effect of **hoisted** installations is that you can accidentally depend on Node modules you haven't specified in your own **package.json**'s `dependencies` or `peerDependencies`. Instead, many more dependencies that other packages rely on are hoisted and become accessible to you. This can cause non-deterministic behavior, and allow you to have broken dependency chains, which are more fragile and can cause resolution errors when updating or upgrading packages. This is especially common in monorepos.
**Starting with SDK 54**, Expo supports isolated dependencies. Unfortunately, not all packages you install will work and some React Native libraries may cause build or resolution errors when used with isolated dependencies. If you encounter issues with isolated installations with [pnpm](https://pnpm.io/settings#nodelinker), switch to the **hoisted** installation strategy by changing the `node-linker` setting in an **.npmrc** file in the root of your repository:
```plain .npmrc
node-linker=hoisted
```
### Duplicate native packages within monorepos
Expo has improved support for more complete **node_modules** patterns, such as isolated modules. Unfortunately, if your app contains duplicate dependencies, issues may still occur:
- Duplicate React Native versions in a single monorepo are not supported
- Duplicate React versions in a single app will cause runtime errors
- Duplicate versions of Turbo and Expo modules may cause runtime or build errors
You can check if your monorepo has multiple versions of a package, for example, `react-native`, and why they're installed through the package manager you use.
<Terminal
  cmd={{
    npm: ['$ npm why react-native'],
    yarn: ['$ yarn why react-native'],
    pnpm: ['$ pnpm why --depth=10 react-native'],
    bun: ['$ bun pm why react-native'],
  }}
/>
The output of these commands will be very different from one package manager to another, but you can spot duplicate packages in any of their outputs by looking for multiple versions of the package, for example `react-native@0.79.5` and `react-native@0.81.0`.
**npm**,
#### Adding dependency resolutions for peer dependencies
If the duplicate dependency is not resolvable by you changing your dependencies, you may have to add a resolution. For example, not all packages have updated their **peerDependencies** to support React 19. To work around this, you can create a resolution to force a single version of `react` to be installed.
```json package.json
{
  "name": "monorepo",
  "private": true,
  "version": "0.0.0",
  "workspaces": ["apps/*", "packages/*"],
  "resolutions": {
    "react": "^19.1.0"
  }
}
```
For [npm](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides), you have to use a property named `overrides` rather than `resolutions`.
#### Deduplicating auto-linked native modules
> **important** This is an alpha feature starting in SDK 54 and later. The process will be automated and have better support in future versions.
Often, duplicate dependencies won't cause any problems. However, native modules should never be duplicated, because only one version of a native module can be compiled for an app build at a time. Unlike JavaScript dependencies, native builds cannot contain two conflicting versions of a single native module.
From **SDK 54**, you can set `experiments.autolinkingModuleResolution` to `true` in your **app.json** to apply autolinking to Expo CLI and Metro bundler automatically. This will force dependencies that Metro resolves to match the native modules that [autolinking](/modules/autolinking/) links for your native builds.
### Script '...' does not exist
React Native uses packages to ship both JavaScript and native files. These native files also need to be linked, like the [**react-native/react.Gradle**](https://github.com/facebook/react-native/blob/v0.70.6/react.gradle) file from **android/app/build.Gradle**. Usually, this path is hardcoded to something like:
**Android** ([source](https://github.com/facebook/react-native/blob/e918362be3cb03ae9dee3b8d50a240c599f6723f/template/android/app/build.gradle#L84))
```groovy
apply from: "../../node_modules/react-native/react.gradle"
```
**iOS** ([source](https://github.com/facebook/react-native/blob/e918362be3cb03ae9dee3b8d50a240c599f6723f/template/ios/Podfile#L1))
```ruby
require_relative '../node_modules/react-native/scripts/react_native_pods'
```
Unfortunately, this path can be different in monorepos because of [hoisting](https://classic.yarnpkg.com/blog/2018/02/15/nohoist/). It also doesn't use the [Node module resolution](https://nodejs.org/api/modules.html#all-together). You can avoid this issue by using Node to find the location of the package instead of hardcoding this:
**Android** ([source](https://github.com/expo/expo/blob/6877c1f5cdca62b395b0d5f49d87f2f3dbb50bec/templates/expo-template-bare-minimum/android/app/build.gradle#L87))
```groovy
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle")
```
**iOS** ([source](https://github.com/expo/expo/blob/61cbd9a5092af319b44c319f7d51e4093210e81b/templates/expo-template-bare-minimum/ios/Podfile#L2))
```ruby
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
```
In the snippets above, you can see that we use Node's own [`require.resolve()`](https://nodejs.org/api/modules.html#requireresolverequest-options) method to find the package location. We explicitly refer to `package.json` because we want to find the root location of the package, not the location of the entry point. And with that root location, we can resolve to the expected relative path within the package. [Learn more about these references here](https://github.com/expo/expo/blob/4633ab2364e30ea87ca2da968f3adaf5cdde9d8b/packages/expo-modules-core/README.mdx#importing-native-dependencies---autolinking).
All Expo SDK modules and templates have these dynamic references and work with monorepos. However, occasionally, you might run into packages that still use the hardcoded path. You can manually edit it with [`patch-package`](https://github.com/ds300/patch-package#readme) or mention this to the package maintainers.


## View logs

Learn how to view logs when using Expo CLI, native logs in Android Studio and Xcode, and system logs.

Logging information in a React Native app works similarly to in a web browser. You can use `console.log`, `console.warn` and `console.error`. However, at times, you might want to dive deep to get more useful information about what's happening in your app. For that, you can use **native logs** and **system logs**.
## Console logs
When you run `npx expo start` and connect a device, console logs will show up in the terminal process. These logs are sent from the runtime to Expo CLI over web sockets, meaning the results are lower fidelity than connecting dev tools directly to the engine.
You can view **high fidelity** logs and use advanced logging functions like `console.table` by creating a development build with [Hermes](/guides/using-hermes), and [connecting the inspector](/guides/using-hermes#javascript-inspector-for-hermes).
## Native logs
You can view native runtime logs in Android Studio and Xcode by compiling the native app locally. For more information, see [native debugging](/debugging/runtime-issues/#native-debugging).
## System logs
While it's usually not necessary, if you want to see logs for everything happening on your device, for example, even the logs from other apps and the OS, you can use the following commands:
```sh
$ npx react-native log-android
$ npx react-native log-ios
```


## Development and production modes

Learn how to run a project in development mode or production mode.

Your project will always run in either **development** or **production** mode. By default, running your project locally with `npx expo start` runs it in development mode, whereas a published project (with `eas update`), or any standalone app, will run in production mode.
**Development mode** includes useful warnings and gives you access to tools that make development and debugging easier. **Production mode** [minifies your code](/guides/customizing-metro/#minification) and better represents the performance your app will have on end users' devices. Let's look at each of these modes in more detail and learn how you can switch between them.
## Development mode
React Native includes some very useful tools for development: remote JavaScript debugging in Chrome, live reload, hot reloading, and an element inspector similar to the beloved inspector you use in Chrome. If you want to see how to use those tools, see [Debugging](/debugging/runtime-issues/).
Development mode also performs validations while your app is running to give you warnings. For example, if you're using a deprecated property or if you forgot to pass a required property into a component. The video below shows the Element Inspector and Performance Monitor in action on both Android Emulator and iOS Simulator:
> **warning** **This comes at a cost. Your app runs slower in development mode.**  You can switch it on and off with the Expo CLI, see [Production mode](#production-mode). When you switch it, close and re-open your app for the change to take effect. **Any time you are testing your app's performance, make sure to disable development mode**.
### View the developer menu
The menu gives access to a host of features that make development and debugging much easier. For more information on how to open it on Android and iOS, see [Developer menu](/debugging/tools/#developer-menu).
## Production mode
Production mode is most useful for two things:
- Testing your app's performance, as Development slows your app down considerably.
- Catching bugs that only show up in production.
The easiest way to simulate how your project will run on end users' devices is with the command:
```sh
$ npx expo start --no-dev --minify
```
It runs the JavaScript of your app in production mode (which tells the Metro bundler to set the `__DEV__` environment variable to `false`, among a few other things). The `--minify` flag minifies your app. This flag also eliminates unnecessary data such as comments, formatting, and unused code. If you are getting an error or crash in your standalone app, running your project with this command can save you a lot of time in finding the root cause.
To completely compile your app for production see [Compiling Android](/more/expo-cli/#compiling-android) and [Compiling iOS](/more/expo-cli/#compiling-ios).


## Common development errors

A list of common development errors that are encountered by developers using Expo.

This page outlines a list of errors that are commonly encountered by developers using Expo. For each error, the first bullet provides an explanation for why the error occurs and the second bullet contains debugging suggestions. If there is an error you think belongs here, we welcome and encourage you to [create a PR](https://github.com/expo/expo/pulls)!
### Metro bundler ECONNREFUSED 127.0.0.1:19001
- An error is preventing the connection to your local development server.
- Run `rm -rf .expo` to clear your local state. Check for firewalls or [proxies](/troubleshooting/proxies/) affecting the network you are currently connected to.
### Module AppRegistry is not a registered callable module (calling runApplication)
- An error in your code is preventing the JavaScript bundle from being executed on startup.
- Try running `npx expo start --no-dev --minify` to reproduce the production JS bundle locally. If possible, connect your device and access the device logs via Android Studio or Xcode. Device logs contain much more detailed stacktraces and information. Check to see if you have any changes or errors in your Babel configuration. In some rare cases, this issue could be caused by incompatibility between the Metro JavaScript minifier and certain code in your app ([more information](https://forums.expo.dev/t/change-minifierconfig-for-minify-uglify/36460/2)).
### npm ERR! No git binary found in \$PATH
- Either you do not have git installed or it is not properly configured in your `$PATH`.
- [Install git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) if you have not already. Otherwise, check how to set it in your `$PATH` based on your OS.
### XX.X.X is not a valid SDK version
- The SDK version you are running has been deprecated and is no longer supported.
- [Upgrade your project](/workflow/upgrading-expo-sdk-walkthrough) to a supported SDK version. If you are using a supported version and see this message, you'll need to update your Expo Go app.
### React Native version mismatch
- The development server running in your terminal is bundling a different version of React Native than the app in your device or simulator.
- [Align your versions of react-native](/troubleshooting/react-native-version-mismatch) by checking the versions in your **app.json** and **package.json**
### Application has not been registered
- There is a mismatch between the AppKey registered in the native and JS portion of your app.
- [Align your AppKey](/troubleshooting/application-has-not-been-registered/) with the native side of your project.
### Application not behaving as expected
- It is possible caches may be preventing you from seeing the current state of your application.
- Clear all caches associated with your project in [Unix-like](/troubleshooting/clear-cache-macos-linux/) or [Windows](/troubleshooting/clear-cache-windows/) systems.


## Android Studio Emulator

Learn how to set up the Android Emulator to test your app on a virtual Android device.

If you don't have an Android device available to test with, we recommend using the default emulator that comes with Android Studio. If you run into any problems setting it up, follow the steps in this guide.
<AndroidStudioEnvironmentInstructions />
<AndroidStudioInstructions />
<AndroidEmulatorInstructions />
## Troubleshooting
### Multiple `adb` versions
Having multiple `adb` versions on your system can result in the following error:
```sh
$ adb server version (xx) doesn't match this client (xx); killing...
```
This is because the `adb` version on your system is different from the `adb` version on the Android SDK platform-tools.
Step 1: 
Open the terminal and check the `adb` version on the system:
```sh
$ adb version
```
Step 2: 
And from the Android SDK platform-tool directory:
```sh
$ cd ~/Library/Android/sdk/platform-tools
$ ./adb version
```
Step 3: 
Copy `adb` from Android SDK directory to `usr/bin` directory:
```sh
$ sudo cp ~/Library/Android/sdk/platform-tools/adb /usr/bin
```


## iOS Simulator

Learn how you can install the iOS Simulator on your Mac and use it to develop your app.

Developing your app directly on a computer can be more convenient than constantly interacting with an iPhone or iPad, especially when network conditions are slow or when a [tunnel connection](/more/expo-cli/#tunneling) is required due to LAN limitations.
This guide explains how to install the iOS Simulator on your Mac for app development. Note that the iOS Simulator can only be installed on macOS. If you are developing an iOS app from a Windows or a Linux machine, you will need a physical iOS device.
## Setup Xcode and Watchman
<XcodeInstructions />
Step 4: 
### Try it out
Run your app with `npx expo start` and press <kbd>i</kbd> from the command line.
You may get a warning about needing to accept the Xcode license. Run the command that it suggests. Open your app again to see if it was successful. If not, check the [troubleshooting](#troubleshooting) tips below.
You can also press <kbd>shift</kbd> + <kbd>i</kbd> in the Expo CLI to interactively select a simulator to open.
## Expo Orbit
You can use the Expo Orbit app which allows launching builds and simulator management with one click from the menu bar on macOS.
## Limitations
Although the iOS Simulator is great for rapid development, it does come with a few limitations. We'll list out a few of the main differences that affect Expo APIs below. However, see [Apple's documentation](https://help.apple.com/simulator/mac/current/#/devb0244142d) for more details.
The following hardware is unavailable in the Simulator:
- Audio Input
- Barometer
- Camera
- Motion Support (accelerometer and gyroscope)
The Simulator also suspends background apps and processes on iOS 11 and later.
## Troubleshooting
### The CLI seems to be stuck when opening a Simulator
Sometimes the iOS Simulator doesn't respond to the open command. If it seems stuck on this prompt, you can open the iOS Simulator manually (`open -a Simulator`) and then in the macOS toolbar, choose **File** &gt; **Open Simulator**, and select an iOS version and device that you'd like to open.
You can use this menu to open any version of the simulator. You can also open multiple simulators at the same time, however, Expo CLI will always target the most recently opened simulator.
### Simulator opened but the Expo Go app isn't opening inside of it
The first time you install the app in the simulator, iOS will ask if you'd like to open the Expo Go app. You may need to interact with the simulator (click around, drag something) for this prompt to show up, then press **OK**.
### How do I force an update to the latest version?
Create a project with the desired SDK version and open it in a simulator to install a particular version of Expo Go.
```sh
$ npx create-expo-app --template blank@53
$ npx expo start --ios
```
### Expo CLI is printing an error message about `xcrun`, what do I do?
For miscellaneous errors, try the following:
- Manually uninstall Expo Go on your simulator and reinstall by pressing <kbd>shift</kbd> + <kbd>i</kbd> in the Expo CLI Terminal UI and selecting the desired simulator.
- If that doesn't help, focus the simulator window and in the Mac toolbar choose **Device** &gt; **Erase All Content and Settings...**
  This will reinitialize your simulator from a blank image. This is sometimes useful for cases where your computer is low on memory and the simulator fails to store some internal files, leaving the device in a corrupt state.


## React Native's New Architecture

Learn about React Native's "New Architecture" and how and why to migrate to it.

> **info** **SDK 55 and later run entirely on the New Architecture.** The New Architecture is always enabled and cannot be disabled. If you need to use the legacy architecture, use SDK 54 or earlier.
The New Architecture is a name that we use to describe a complete refactoring of the internals of React Native. It is also used to solve limitations of the original React Native architecture discovered over years of usage in production at Meta and other companies.
In this guide, we'll talk about how to use the New Architecture in Expo projects today.
Note: Why migrate to the New Architecture?
---
**The New Architecture is the present and future of React Native**. Starting with React Native 0.82, the New Architecture is always enabled and cannot be disabled. SDK 55 uses React Native 0.83, which inherits this behavior. The [legacy architecture was frozen](https://github.com/reactwg/react-native-new-architecture/discussions/290) in June 2025, meaning no new features or bugfixes are being developed for it.
**New React and React Native features are coming to the New Architecture only**. For example, the New Architecture includes [full support for Suspense](https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here#full-support-for-suspense) and [new styling capabilities](https://reactnative.dev/blog/2025/01/21/version-0.77#new-css-features-for-better-layouts-sizing-and-blending) that are not implemented in the legacy architecture. Many popular libraries now only support the New Architecture.
**If you are on SDK 54 or earlier**, you can still use the legacy architecture by setting `newArchEnabled` to `false`. However, you will need to migrate to the New Architecture before upgrading to SDK 55 or later.
---
## Expo tools and the New Architecture
As of SDK 53, all `expo-*` packages in the [Expo SDK](/versions/latest/) support the New Architecture (including [bridgeless](https://github.com/reactwg/react-native-new-architecture/discussions/154)). [Learn more about known issues](#known-issues-in-expo-sdk-libraries).
Additionally, all modules written using the [Expo Modules API](/modules/overview/) support the New Architecture by default! So if you have built your own native modules using this API, no additional work is needed to use them with the New Architecture.
**As of January 2026, approximately 83% of SDK 54 projects built with [EAS Build](/build/introduction/) use the New Architecture**.
## Third-party libraries and the New Architecture
The compatibility status of many of the most popular libraries is tracked on [React Native Directory](https://reactnative.directory/) ([learn more about known issues in third-party libraries](#known-issues-in-third-party-libraries)). We've built tooling into Expo Doctor to integrate with React Native Directory to help you validate your dependencies, so you can quickly learn which libraries are unmaintained and which incompatible or untested with the New Architecture.
### Validate your dependencies with React Native Directory
Run `npx expo-doctor` to check your dependencies against the data in React Native Directory.
```sh
$ npx expo-doctor@latest
```
You can configure the React Native Directory check in your **package.json** file. For example, if you would like to exclude a package from validation:
```json package.json
{
  "expo": {
    "doctor": {
      "reactNativeDirectoryCheck": {
        "exclude": ["react-redux"]
      }
    }
  }
}
```
Note: See all available options
---
- **enabled**: If `true`, the check will warn if any packages are missing from React Native Directory. Set this to `false` to disable this behavior. In SDK 52 and later, this is set to `true` by default, otherwise it is `false` by default. You can also override this setting with the `EXPO_DOCTOR_ENABLE_DIRECTORY_CHECK` environment variable (0 is `false`, 1 is `true`).
- **exclude**: List any packages you want to exclude from the check. Supports exact package names and regex patterns. For example, `["exact-package", "/or-a-regex-.*/"]`.
- **listUnknownPackages**: By default, the check will warn if any packages are missing from React Native Directory. Set this to false to disable this behavior.
---
## Initialize a new project with the New Architecture
**As of SDK 52**, all new projects will be initialized with the New Architecture enabled by default.
```sh
$ npx create-expo-app@latest
```
## Enable the New Architecture in an existing project
  For SDK 55 and later: 
    **The New Architecture is always enabled in SDK 55 and later**. There is no option to disable it. SDK 55 uses React Native 0.83. [React Native 0.82 was the first version to remove the option to disable the New Architecture](https://reactnative.dev/blog/2025/10/08/react-native-0.82), and this applies to all later versions.
    If you were previously using `newArchEnabled: false` in your app config, this setting will be ignored. Remove it from your configuration to avoid confusion.
  For SDK 53 and SDK 54: 
    **The New Architecture is enabled by default in SDK 53 and SDK 54**. If you have explicitly disabled it, remove that configuration to enable it.
    > **info** SDK 54 is the last SDK version where the New Architecture can be disabled.
  For SDK 52: 
    We recommend upgrading to SDK 53 to ensure that your app can take advantage of all of the latest New Architecture related fixes and improvements with libraries and React Native itself. If you want to try it on SDK 52 first, follow the instructions below.
    Step 1: 
    To enable it on both Android and iOS, use the `newArchEnabled` at the root of the `expo` object in your app config. You can selectively enable it for a single platform by setting, for example, `"android": { "newArchEnabled": true }`.
    ```json app.json
    {
      "expo": {
        "newArchEnabled": true
      }
    }
    ```
    Step 2: 
      Create a new build:
          ```sh
if you like
$ npx expo prebuild --clean && npx expo run:android
$ eas build -p android
```
          ```sh
if you like
$ npx expo prebuild --clean && npx expo run:ios
$ eas build -p ios
```
      If the build succeeds, you will now be running your app with the New Architecture! Depending on the native modules you use, your app may work properly immediately.
      Now you can tap around your app and test it out. For most non-trivial apps, you're likely to encounter some issues, such as missing native views that haven't been implemented for the New Architecture yet. Many of the issues you encounter are actionable and can be resolved with some configuration or code changes. We recommend reading [Troubleshooting](#troubleshooting) sections below for more information.
  For SDK 51 and earlier: 
    We recommend upgrading to SDK 54 or SDK 55. SDK 51 is significantly outdated, and the [legacy architecture has been frozen](https://github.com/reactwg/react-native-new-architecture/discussions/290), meaning it will not receive new features or bugfixes. It's possible to enable the New Architecture in SDK 51, but you are likely to encounter a variety of issues that have been resolved in more recent releases. To enable it, you need to [install the `expo-build-properties` plugin](/versions/latest/sdk/build-properties/#installation) and set `newArchEnabled` on target platforms.
Note: Are you enabling the New Architecture in a bare React Native app?
---
If you are using Expo SDK 53 or later, the New Architecture is enabled by default. For SDK 55 and later, the New Architecture is always enabled and cannot be disabled. The following instructions apply to SDK 52 and earlier projects.
- **Android**: Set `newArchEnabled=true` in the **gradle.properties** file.
- **iOS**: If your project has a **Podfile.properties.json** file (which is created by `npx create-expo-app` or `npx expo prebuild`), you can enable the New Architecture by setting the `newArchEnabled` property to `"true"` in the **Podfile.properties.json** file. Otherwise, refer to the ["Enable the New Architecture for Apps"](https://github.com/reactwg/react-native-new-architecture/blob/main/docs/enable-apps.md) section of the React Native New Architecture working group.
---
## Disable the New Architecture in an existing project
> **warning** **SDK 55 and later do not support disabling the New Architecture.** SDK 55 uses React Native 0.83. Starting with [React Native 0.82, the option to disable the New Architecture was removed](https://reactnative.dev/blog/2025/10/08/react-native-0.82), so setting `newArchEnabled` to `false` has no effect. If you need to use the legacy architecture, use SDK 54 or earlier.
> **info** Expo Go only supports the New Architecture.
On **SDK 54 and earlier**, you can opt out of the New Architecture by setting the `newArchEnabled` property to `false` in app config and create a [development build](/develop/development-builds/introduction/).
```json app.json
{
  "expo": {
    "newArchEnabled": false
  }
}
```
Note: Are you disabling the New Architecture in a bare React Native app (SDK 54 and earlier)?
---
- **Android**: Set `newArchEnabled=false` in the **gradle.properties** file.
- **iOS**: If your project has a **Podfile.properties.json** file (which is created by `npx create-expo-app` or `npx expo prebuild`), you can disable the New Architecture by setting the `newArchEnabled` property to `"false"` in the **Podfile.properties.json** file. Otherwise, refer to the ["Enable the New Architecture for Apps"](https://github.com/reactwg/react-native-new-architecture/blob/main/docs/enable-apps.md) section of the React Native New Architecture working group.
---
## Troubleshooting
Meta and Expo are working toward making the New Architecture the default for all new apps and ensuring it is as easy as possible to migrate existing apps. However, the New Architecture isn't just a name &mdash; many of the internals of React Native has been re-architected and rebuilt from the ground up. As a result, you may encounter issues when enabling the New Architecture in your app. The following is some advice for troubleshooting these issues.
Note: Can I still try the New Architecture even if some of the libraries I use aren't supported?
---
You may be able to try the New Architecture in your app even if some of the libraries you use aren't supported, but it will require temporarily removing those libraries. Create a new branch in your repository and remove any of the libraries that aren't compatible until your app is running. This will give you a good idea of what libraries still need work before you can fully migrate to the New Architecture. We recommend creating issues or pull requests on those libraries' repositories to help them become compatible with the New Architecture. Alternatively, you could switch to other libraries that are compatible with the New Architecture. Refer to [React Native Directory](https://reactnative.directory/) to find compatible libraries.
---
Note: Known issues in React Native
---
Refer to the [issues labeled with "Type: New Architecture" on the React Native GitHub repository](https://github.com/facebook/react-native/issues?q=is%3Aopen+is%3Aissue+label%3A%22Type%3A+New+Architecture%22).
---
Note: Known issues in Expo libraries
---
There are no known issues specific to the New Architecture in Expo libraries.
---
Note: Known issues in third-party libraries
---
Since React Native 0.74, there are various Interop Layers enabled by default. This allows many libraries built for the old architecture to work on the New Architecture without any changes. However, the interop is not perfect and some libraries will need to be updated. The libraries that are most likely to require updates are those that ship or depend on third-party native code. [Learn more about library support in the New Architecture](https://github.com/reactwg/react-native-new-architecture/discussions/167).
Refer to [React Native Directory](https://reactnative.directory/) a more complete list of libraries and their compatibility with the New Architecture. The following libraries were found to be popular among Expo apps and are known to be incompatible:
The following are known issues with libraries that are popular among Expo apps.
- **react-native-maps**: Version 1.20.x, which is the default for SDK 53, supports the New Architecture with the interop layer and works well for most features. A New Architecture-first version is available in version 1.21.0, which is still stabilizing. We encourage your to test it in your app, report issues that you find, and [follow along with the discussion on GitHub](https://github.com/react-native-maps/react-native-maps/discussions/5355). We are also investigating another approach that may provider a smoother migration path, by leaning on the [interop layer](https://github.com/reactwg/react-native-new-architecture/discussions/175) rather than rewriting the module. It's worth mentioning that if your app can force a minimum version of iOS 17, or does not need to support maps on iOS, then you can consider using [`expo-maps`](/versions/latest/sdk/maps/) instead.
- **@stripe/react-native**: The New Architecture is supported starting with version 0.45.0, which is the default for SDK 53.
- **@react-native-community/masked-view**: Use `@react-native-masked-view/masked-view` instead.
- **@react-native-community/clipboard**: Use `@react-native-clipboard/clipboard` instead.
- **rn-fetch-blob**: Use `react-native-blob-util` instead.
- **react-native-fs**: Use `expo-file-system` or [a fork of react-native-fs](https://github.com/birdofpreyru/react-native-fs) instead.
- **react-native-geolocation-service**: Use `expo-location` instead.
- **react-native-datepicker**: Use `react-native-date-picker` or `@react-native-community/datetimepicker` instead.
---
Note: My build failed after enabling the New Architecture
---
This isn't entirely surprising! Not all libraries are compatible yet, and in some cases compatibility was only recently added and so you will want to ensure you update your libraries to their latest versions. Read the logs to determine which library is incompatible. Also, run `npx expo-doctor@latest` to check your dependencies against the data in React Native Directory.
When you are using the latest version of a library and it is not compatible, report any issues you encounter to the respective GitHub repository. Create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) and report the issue to the library author. If you believe the issue originates in React Native itself, rather than a library, report it to the React Native team (again, with a minimal reproducible example).
---


## React Compiler

Learn how to enable and use the React Compiler in Expo apps.

The new [React Compiler](https://react.dev/learn/react-compiler) automatically memoizes components and hooks to enable fine-grained reactivity. This can lead to significant performance improvements in your app. You can enable it in your app by following the instructions below.
## Enabling React Compiler
Step 1: 
[Check how compatible](https://react.dev/learn/react-compiler#checking-compatibility) your project is with the React Compiler.
```sh
$ npx react-compiler-healthcheck@latest
```
This will generally verify if your app is following the [**rules of React**](https://react.dev/reference/rules).
Step 2: 
Install `babel-plugin-react-compiler` and the React compiler runtime in your project:
    Babel is automatically configured in Expo SDK 54 and later.
    ```sh
$ npx expo install babel-plugin-react-compiler@beta
```
    ```sh
$ npx expo install babel-plugin-react-compiler@beta react-compiler-runtime@beta
```
Step 3: 
Toggle on the React Compiler experiment in your app config file:
```json app.json
{
  "expo": {
    "experiments": {
      "reactCompiler": true
    }
  }
}
```
### Enabling the linter
> In the future, all of the following steps below will be automated by Expo CLI.
Additionally, you should use the ESLint plugin to continuously enforce the rules of React in your project.
Step 1: 
Run [`npx expo lint`](/guides/using-eslint/#eslint) to ensure ESLint is setup in your app, then install the ESLint plugin for React Compiler:
```sh
$ npx expo install eslint-plugin-react-compiler -- -D
```
Step 2: 
Update your [ESLint configuration](/guides/using-eslint/) to include the plugin:
```js .eslintrc.js
// https://docs.expo.dev/guides/using-eslint/
const { defineConfig } = require('eslint/config');
const expoConfig = require('eslint-config-expo/flat');
const reactCompiler = require('eslint-plugin-react-compiler');
module.exports = defineConfig([
  expoConfig,
  reactCompiler.configs.recommended,
  {
    ignores: ['dist/*'],
  },
]);
```
## Incremental adoption
You can incrementally adopt the React Compiler in your app using a few strategies:
Step 1: 
Configure the Babel plugin to only run on specific files or components. To do this:
1. If your project doesn't have [**babel.config.js**](/versions/latest/config/babel/), create one by running `npx expo customize babel.config.js`.
2. Add the following configuration to **babel.config.js**:
```js babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      [
        'babel-preset-expo',
        {
          'react-compiler': {
            sources: filename => {
              // Match file names to include in the React Compiler.
              return filename.includes('src/path/to/dir');
            },
          },
        },
      ],
    ],
  };
};
```
Whenever you change your **babel.config.js** file, you need to restart the Metro bundler to apply the changes:
```sh
$ npx expo start --clear
```
Step 2: 
Use the `"use no memo"` directive to opt out of the React Compiler for specific components or files.
```jsx
function MyComponent() {
  'use no memo';
  return <Text>Will not be optimized</Text>;
}
```
## Usage
> To better understand how React Compiler works, check out the [React Playground](https://playground.react.dev/).
Improvements are primarily automatic. You can remove instances of `useCallback`, `useMemo`, and `React.memo` in favor of the automatic memoization. Class components will not be optimized. Instead, migrate to function components.
Expo's implementation of the React Compiler will only run on application code (no node modules), and only when bundling for the client (disabled in server rendering).
## Configuration
You can pass additional settings to the React Compiler Babel plugin by using the `react-compiler` object in the Babel configuration:
```js babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      [
        'babel-preset-expo',
        {
          'react-compiler': {
            // Passed directly to the React Compiler Babel plugin.
            compilationMode: 'all',
            panicThreshold: 'all_errors',
          },
          web: {
            'react-compiler': {
              // Web-only settings...
            },
          },
        },
      ],
    ],
  };
};
```