Expo config plugin extending native projects for building app as a brownfield.
npm install expo-brownfield-target!expo-brownfield-target by Software Mansion
> [!WARNING]
> This library is in early development stage; breaking changes can be introduced in minor version upgrades.
expo-brownfield-target is a library which includes an Expo config plugin that automates brownfield setup in the project, CLI for building the brownfield artifacts and built-in APIs for communication and navigation between the apps.
- Android Publishing
- CLI
- Communication API
- Configuration
- Manual Setup
- Navigation API
- Templates
- Motivation
- Features
- Platform & Expo SDK compatibility
- Usage
- Installation
- Plugin setup
- Manual setup
- Adding brownfield targets
- Building with CLI
- Building manually
- Using built artifacts in native projects
- Android
- iOS (SwiftUI)
- iOS (UIKit)
- Using with Metro Bundler
- Acknowledgments
Brownfield approach enables integrating React Native apps into native Android and iOS projects, but setting it up, especially in Expo projects using Continuous Native Generation is a manual, repetitive, and pretty complex task.
This library aims to fully automate and simplify brownfield setup by including a config plugin that configures your project on every prebuild, built-in APIs for common use cases and CLI which builds the brownfield artifacts.
Such setup of brownfield allows for easy publishing to Maven, as XCFramework or using Swift Package Manager which enables e.g. simple and more independent cooperation of native and RN teams.
- Automatic extension of native projects with brownfield targets
- Easy integration with Expo project via config plugin interface
- Artifact publishing using XCFramework for iOS and Maven for Android
- Configurability & customizability
- APIs for bi-directional communication and navigation between the apps
The plugin supports both Android and iOS. As of now we only support Expo SDK 54.
``sh`
npm install expo-brownfield-target
Add the config plugin to the "plugins" section in your app.json or app.config.js / app.config.ts:
`json`
{
"expo": {
"name": "my-awesome-expo-project",
...
"plugins": [
... // Other plugins
"expo-brownfield-target"
]
}
}
If you want to pass any configuration options make sure to add the plugin as an array:
`json`
{
"expo": {
"name": "my-awesome-expo-project",
...
"plugins": [
... // Other plugins
[
"expo-brownfield-target",
{
"android": {
...
},
"ios": {
...
}
}
]
]
}
}
See configuration.md for full reference of configurable options.
All steps performed by the plugin can also be performed manually. Please refer to manual-setup.md for a full guide for manual setup.
The additional targets for brownfield will be added automatically every time you prebuild the native projects:
`sh`
npx expo prebuild
The plugin comes with a built-in CLI which can be used to build both Android and iOS targets:
`sh
npx expo-brownfield-target build-android -r MavenLocal
npx expo-brownfield-target build-ios
`
More details and full reference of the CLI commands and options can be found in cli.md.
Brownfields can be also built manually using the xcodebuild and ./gradlew commands.
` bashCompile the framework
xcodebuild \
-workspace "ios/myexpoapp.xcworkspace" \
-scheme "MyBrownfield" \
-derivedDataPath "ios/build" \
-destination "generic/platform=iphoneos" \
-destination "generic/platform=iphonesimulator" \
-configuration "Release"
`bash
./gradlew publishBrownfieldAllPublicationToMavenLocal
`See publishing.md for more details about the publishing tasks.
$3
Below snippets are taken from the examples of using brownfields inside native apps at: example/android, example/ios and example/ios-swiftui.
$3
`kotlin
// MainActivity.kt
package com.swmansion.exampleimport android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
import com.swmansion.brownfield.showReactNativeFragment
class MainActivity : AppCompatActivity(), DefaultHardwareBackBtnHandler {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
showReactNativeFragment()
}
override fun invokeDefaultOnBackPressed() {
// ...
}
}
`$3
`swift
// ContentView.swift
import SwiftUI
import MyBrownfieldAppstruct ContentView: View {
init() {
ReactNativeHostManager.shared.initialize()
}
var body: some View {
VStack {
ReactNativeView(moduleName: "main")
}
}
}
`$3
`swift
// AppDelegate.swift
import UIKit
import MyBrownfieldApp@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
ReactNativeHostManager.shared.initialize()
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ReactNativeViewController(moduleName: "main")
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
}
`$3
Debug builds use bundle hosted by Metro server (hosted over
localhost:8081) instead of the bundle included in the brownfield framework.Be sure to start Metro server by running the following command in your Expo project:
`
npm start
`#### Android
To be able to use Metro create a separate debug-only Manifest with the following contents in your native app which will host the brownfield:
`xml
xmlns:tools="http://schemas.android.com/tools">
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:usesCleartextTraffic"
tools:targetApi="28" />
`Then be sure to build and publish the artifacts using either
All (includes both debug and release) or Debug configuration and to use the debug variant in the native app. Also, don't forget to reverse the port 8081 (if necessary):`
adb reverse tcp:8081 tcp:8081
`#### iOS
To use Metro server instead of bundle included at the build time, compile the brownfield framework using
Debug configuration (-d/--debug flag when using the CLI). Debug` XCFramework should automatically source the bundle from the Metro server.Huge thanks to:
- @hurali97 for shipping some of the work we built this on
- @lukmccall, @aleqsio and @dawidmatyjasik for research and support during the plugin development
- @callstack for shipping the great set of helpers for brownfields (react-native-brownfield) which inspired parts of this library

Since 2012 Software Mansion is a software agency with
experience in building web and mobile apps. We are Core React Native
Contributors and experts in dealing with all kinds of React Native issues. We
can help you build your next dream product –
Hire us.
Made by @software-mansion and
community 💙