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.
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.
npm install expo-brownfield-targetAdd the config plugin to the "plugins" section in your app.json or app.config.js / app.config.ts:
{
"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:
{
"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:
npx expo prebuildThe plugin comes with a built-in CLI which can be used to build both Android and iOS targets:
npx expo-brownfield-target build-android -r MavenLocal
npx expo-brownfield-target build-iosMore 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.
# Compile the framework
xcodebuild \
-workspace "ios/myexpoapp.xcworkspace" \
-scheme "MyBrownfield" \
-derivedDataPath "ios/build" \
-destination "generic/platform=iphoneos" \
-destination "generic/platform=iphonesimulator" \
-configuration "Release"
# Package it as an XCFramework
xcodebuild \
-create-xcframework \
-framework "ios/build/Build/Products/Release-iphoneos/MyBrownfield.framework" \
-framework "ios/build/Build/Products/Release-iphonesimulator/MyBrownfield.framework" \
-output "artifacts/MyBrownfield.xcframework"./gradlew publishBrownfieldAllPublicationToMavenLocalSee publishing.md for more details about the publishing tasks.
Below snippets are taken from the examples of using brownfields inside native apps at: example/android, example/ios and example/ios-swiftui.
// MainActivity.kt
package com.swmansion.example
import 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() {
// ...
}
}// ContentView.swift
import SwiftUI
import MyBrownfieldApp
struct ContentView: View {
init() {
ReactNativeHostManager.shared.initialize()
}
var body: some View {
VStack {
ReactNativeView(moduleName: "main")
}
}
}// 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
}
}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
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:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:usesCleartextTraffic"
tools:targetApi="28" />
</manifest>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
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 💙
