# Existing native apps

## Integrating Expo tools into existing native apps

An overview of how you can integrate Expo tools into existing native apps ("brownfield" apps).

An existing native app that was built using another technology, whose main entry point is _not_ a React Native view, is commonly referred to as a "brownfield" app. For example, if your app was built using UIKit and Swift, and you want to use React Native for a single screen then that is considered an "existing native app" and "brownfield".
In contrast, "greenfield" apps are created using Expo or React Native from the start or where React Native is the entry point and where all other UI branches off from.
By these definitions, if you have an "existing native app" for Android or iOS and you want to learn how to use Expo and React Native in your project (perhaps on a single screen or even a single feature), then this guide is for you.
## Compatibility with existing native apps
> **info** Support for integrating Expo modules into existing native projects is in alpha. If you encounter issues, [create an issue on GitHub](https://github.com/expo/expo/issues). Not all features of the tools and services below will be available when used in the context of an existing native app.
Expo is primarily built with greenfield apps in mind, but we are increasingly investing in brownfield scenarios. Not all Expo tools and services are compatible with existing native projects yet. Additionally, comprehensive documentation for brownfield integrations may not yet available, and you may need to adapt other related documentation to your context.
| Tool/Service                                                                                         | Supports brownfield? |
| ---------------------------------------------------------------------------------------------------- | -------------------- |
| [Expo SDK](/versions/latest/) - an extended standard library for React Native                        | Yes                  |
| [Expo Modules API](/modules/overview/) - build native extensions using an idiomatic Swift/Kotlin API | Yes                  |
| [Expo Router](/router/introduction/) - file-based routing and navigation                             | No                   |
| [Expo CLI](/more/expo-cli/) - tools to run and develop your app from your terminal                   | Yes                  |
| [Expo Dev Client](/versions/latest/sdk/dev-client/) - adds in-app developer tooling to Debug builds  | No                   |
| [EAS Build](/build/introduction/) - a CI/CD service built specifically for Expo/React Native         | Yes                  |
| [EAS Submit](/submit/introduction/) - a hosted service that uploads your app to stores               | Yes                  |
| [EAS Update](/eas-update/introduction/) - instant updates of your app JavaScript and assets          | Yes                  |
## Integrated vs Isolated approaches
When you integrate React Native into an existing native app, you can choose between two main approaches: integrated and isolated. The best approach for you will depend on your project's structure, your team's workflow, and your long-term goals.
### Integrated approach
In the integrated approach, your React Native code lives inside your existing native project. This allows for a tight coupling between your React Native and native code.
For example, you might add your existing Android or iOS native projects to a subdirectory of the React Native project. This is a common setup for projects that started with React Native and added native code later, but it can also be used for existing native apps. If you can't use the standard `android` and `ios` subdirectories for your native projects, you can configure a custom root folder for your React Native code, with a simple monorepo setup.
**Choose this approach if:**
- You need to frequently iterate on both native and React Native code together.
- You have a single team that manages both native and React Native development.
- Your project structure allows for adding a React Native project directly.
### Isolated approach
In the isolated approach, your React Native code is developed and maintained separately from your native project, and it can be maintained in a separate repository or in a monorepo.
With this approach, you package your React Native app as a native library (using AAR for Android and XCFramework for iOS). Then, you integrate this library into your native app just like any other native dependency.
This separation simplifies the workflow for native developers, as they don't need to set up a Node.js environment, or deal with React Native's build dependencies. They can just consume the React Native part of the app as a pre-built artifact.
**Choose this approach if:**
- You have separate teams for native and React Native development.
- You want to minimize the impact of adding React Native on your existing native build process.
- You prefer to treat the React Native part of your app as a self-contained module.
## Next steps


## How to add Expo to an existing native (brownfield) app

A guide for adding Expo and React Native to existing native apps and adding a first view component.

React Native and Expo are flexible and can be adopted incrementally, one screen (or even one view) at a time. You might even find that using Expo in this way is the best fit for your particular application, or you may end up slowly adopting it across more surfaces in your app. Either way, this flexibility allows enables developers to adopt modern, cross-platform tools in their native apps immediately instead of risking a complete rewrite.
This guide will walk you through the steps to add a React Native view into an existing native app. The approach covered here is what we call the "integrated" approach, because React Native and Expo are integrated in the same way that you would any other library.
Another popular technique is what we call the "isolated" approach, where your Expo app is packaged as a library and treated as a black box by the main existing application. A guide for the isolated approach will be available shortly. Now, back to using the "integrated" approach in your existing native app.
## Prerequisites
To integrate React Native into your existing application, you'll need to set up a JavaScript development environment. This includes installing Node.js to run Expo CLI and Yarn to manage the project's JavaScript dependencies.
- [Node.js (LTS)](https://nodejs.org/en/): The runtime to execute JavaScript code and Expo CLI.
- [Yarn](https://yarnpkg.com/): A package manager for installing and managing JavaScript dependencies.
- <PlatformTag platform="ios" /> [CocoaPods](https://cocoapods.org/): one of the dependency
  management system available for iOS. CocoaPods is a Ruby
  [gem](https://en.wikipedia.org/wiki/RubyGems). You can install CocoaPods using the version of Ruby
  that ships with the latest version of macOS.
Learn more from the [Set up environment guide](/get-started/set-up-your-environment/).
## Create an Expo project
First, create an Expo project inside your existing native project's root directory.
```sh
$ npx create-expo-app my-project
```
This command creates a new directory named **my-project** that contains your new Expo project. While you can name the project anything, this guide uses **my-project** for consistency. The new project includes an example TypeScript application to help you get started.
## Set up your project structure
A standard React Native project places native code in **android** and **ios** directories. The specifics of how to do this depend on your project, but it could be as simple as creating the directories and moving your projects there. For example:
For Android: 
```sh
$ mkdir my-project/android
$ mv /path/to/your/android-project my-project/android/
```
For iOS: 
```sh
$ mkdir my-project/ios
$ mv /path/to/your/ios-project my-project/ios/
```
Note: Can't move your native projects to android and ios directories?
---
### Set up a monorepo
Monorepos, or "monolithic repositories", are single repositories containing multiple apps or packages. [Learn more](/guides/monorepos/).
Setting up a monorepo will ensure that Android and iOS scripts will be able to invoke commands from Node libraries even with a custom folder structure. To set up a Yarn monorepo, create a **package.json** file at the root of your project and add the following content:
```json package.json
{
  "version": "1.0.0",
  "private": true,
  "workspaces": ["my-project"]
}
```
Then run `yarn install` to install the dependencies. This will ensure **node_modules** are installed at the root of your project, and that native scripts can interact with React Native code. Make sure to change `["my-project"]` to the name of the Expo project you created in the previous step.
> **info** Opting for a monorepo requires you to configure a custom project root, in Gradle/CocoaPods. This will be covered in the next sections.
---
## Configuring your native project
To integrate React Native on Android, you need to configure the native project by modifying the following files:
- **Gradle files**: **settings.gradle**, top-level **build.gradle**, **app/build.gradle**, and **gradle.properties** to add the React Native Gradle Plugin (RNGP) and other properties.
- **AndroidManifest.xml**: To add necessary permissions. ([Learn more](#configuring-your-manifest))
- **MainActivity**: To load your React Native application.
### Configuring Gradle
Step 1: 
Start by editing your **settings.gradle** file and add the following lines (Use the [bare minimum template](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/settings.gradle) as a reference):
```groovy settings.gradle
// Configures the React Native Gradle Settings plugin used for autolinking
pluginManagement {
  def reactNativeGradlePlugin = new File(
    providers.exec {
      workingDir(rootDir)
      commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })")
    }.standardOutput.asText.get().trim()
  ).getParentFile().absolutePath
  includeBuild(reactNativeGradlePlugin)
  def expoPluginsPath = new File(
    providers.exec {
      workingDir(rootDir)
      commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })")
    }.standardOutput.asText.get().trim(),
    "../android/expo-gradle-plugin"
  ).absolutePath
  includeBuild(expoPluginsPath)
}
plugins {
  id("com.facebook.react.settings")
  id("expo-autolinking-settings")
}
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
  ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
}
expoAutolinking.useExpoModules()
// rootProject.name = 'HelloWorld'
expoAutolinking.useExpoVersionCatalog()
includeBuild(expoAutolinking.reactNativeGradlePlugin)
// Include your existing Gradle modules here.
// include(":app")
```
Note: Using a custom folder structure?
---
If you're using a custom folder structure, you need to explicitly set your project root in **settings.gradle** for autolinking to work. Modify the following lines:
<DiffBlock source="/static/diffs/brownfield/android/settings-gradle-custom-root.diff" />
---
Step 2: 
Then open your top-level **build.gradle** and include this line (as suggested from the [bare minimum template](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/build.gradle)):
<DiffBlock source="/static/diffs/brownfield/android/build-gradle.diff" />
This makes sure the React Native Gradle and the Expo plugins are available and applied inside your project.
Step 3: 
Add the following lines inside your app's **build.gradle** file (usually **app/build.gradle** &mdash; you can use the [bare minimum template file as reference](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/build.gradle)):
<DiffBlock source="/static/diffs/brownfield/android/app-build-gradle.diff" />
Note: Using a custom folder structure?
---
If you're using a custom folder structure, you need to adjust the `projectRoot` value to point to root of your Expo project in **app/build.gradle**. Modify the following lines:
<DiffBlock source="/static/diffs/brownfield/android/app-build-gradle-custom-root.diff" />
---
Step 4: 
Finally, open your app's **gradle.properties** file and add the following lines (use the [bare minimum template file as reference](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/gradle.properties)):
```properties gradle.properties
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
newArchEnabled=true
hermesEnabled=true
```
### Configuring your manifest
Step 1: 
First, make sure you have the `INTERNET` permission in your **AndroidManifest.xml**:
<DiffBlock source="/static/diffs/brownfield/android/android-manifest-internet-permission.diff" />
Step 2: 
Now in your **debug** **AndroidManifest.xml**, enable [cleartext traffic](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted):
<DiffBlock source="/static/diffs/brownfield/android/android-manifest-cleartext-traffic.diff" />
This is necessary for your app to communicate with your local [Metro bundler](https://metrobundler.dev/) via HTTP. You can use the **AndroidManifest.xml** files from the bare minimum template as a reference: [main](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/src/main/AndroidManifest.xml) and [debug](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/src/debug/AndroidManifest.xml)
### Integrating with your code
Now, you need to add some native code to start the React Native runtime and tell it to render your React components.
#### Updating your `Application` class
Start by updating your `Application` class to initialize React Native. You can use **MainApplication.kt** from the [bare minimum template](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/src/main/java/com/helloworld/MainApplication.kt) as a reference:
<DiffBlock source="/static/diffs/brownfield/android/main-application.diff" />
#### Creating a `ReactActivity`
Create a new `Activity` that will extend `ReactActivity` and host the React Native code. This activity will be responsible for starting the React Native runtime and rendering the React component. You can use the [**MainActivity.kt** from bare minimum template file](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/src/main/java/com/helloworld/MainActivity.kt) as a reference:
```kotlin MyReactActivity.kt
// package <your-package-here>
import android.os.Build
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
import expo.modules.ReactActivityDelegateWrapper
class MyReactActivity : ReactActivity() {
  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  override fun getMainComponentName(): String = "main"
  /**
   * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
   * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
   */
  override fun createReactActivityDelegate(): ReactActivityDelegate {
    return ReactActivityDelegateWrapper(
          this,
          BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
          object : DefaultReactActivityDelegate(
              this,
              mainComponentName,
              fabricEnabled
          ){})
  }
}
```
Add the new Activity to your **AndroidManifest.xml** file, make sure to set the theme of `MyReactActivity` to `Theme.AppCompat.Light.NoActionBar` (or to any non-ActionBar theme) to avoid your application rendering an `ActionBar` on top of the React Native screen:
<DiffBlock source="/static/diffs/brownfield/android/android-manifest-add-activity.diff" />
Now your activity is ready to run some JavaScript code.
To integrate React Native on iOS, you need to configure the native iOS project by modifying the following files:
- **Podfile**: To add the React Native dependencies.
- **Xcode project**: To add a build phase for bundling JavaScript code.
- **Info.plist**: To configure app settings required by React Native.
### Configuring CocoaPods
If your project does not have a **Podfile**, you can create one using the [bare minimum template](https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/ios/Podfile) as a reference:
```rb Podfile
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require 'json'
platform :ios, '15.1'
install! 'cocoapods',
  :deterministic_uuids => false
prepare_react_native_project!
target 'HelloWorld' do
  use_expo_modules!
  config_command = [
    'npx',
    'expo-modules-autolinking',
    'react-native-config',
    '--json',
    '--platform',
    'ios'
  ]
  config = use_native_modules!(config_command)
  use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
  use_react_native!(
    :path => config[:reactNativePath],
    :hermes_enabled => true,
    # An absolute path to your application root.
    :app_path => "#{Pod::Config.instance.installation_root}/..",
    :privacy_file_aggregation_enabled => true,
  )
  post_install do |installer|
    react_native_post_install(
      installer,
      config[:reactNativePath],
      :mac_catalyst_enabled => false,
    )
  end
end
```
If your project already has a **Podfile**, you'll need to manually merge the React Native dependencies into your existing **Podfile**.
Note: Using a custom folder structure?
---
If you're using a custom folder structure, you need to explicitly set your project root in **Podfile** for autolinking to work. Modify the following lines in your **Podfile**:
<DiffBlock source="/static/diffs/brownfield/ios/podfile-custom-root.diff" />
---
Now, run the following command:
```sh
$ pod install
```
Running the `pod` command will integrate the React Native code into your app, allowing your iOS files to import the React Native headers.
### Configuring your Xcode project
Step 1: 
After the `pod install` command, CocoaPods will create an Xcode workspace **\{Project\}.xcworkspace**, you will need to open the **xcworkspace** project than the traditional **xcodeproj** project. Alternatively, you can use the following command to open the project:
```sh
$ xed my-project/ios
```
In the Xcode project navigator, select your project and then select
your app target under `TARGETS`. In **Build Settings**, using the search bar, search for
`ENABLE_USER_SCRIPT_SANDBOXING`. If it is not already, set its value to `No`. This is needed to
properly switch between the Debug and Release versions of the [Hermes
engine](https://github.com/facebook/hermes/blob/main/README.md) that is shipped with React Native.
Step 2: 
Now switch to the **Build Phases** tab and add a new `Run Script Phase` before the `[CP] Embed Pods Frameworks` phase. This script will bundle your JavaScript code and assets into the iOS application.
```sh Build React Native code and image
if [[ -f "$PODS_ROOT/../.xcode.env" ]]; then
  source "$PODS_ROOT/../.xcode.env"
fi
if [[ -f "$PODS_ROOT/../.xcode.env.local" ]]; then
  source "$PODS_ROOT/../.xcode.env.local"
fi