Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
•••••••••
AI & KI NachrichtenOpenAI’s Dots: A First Look(02.10.2026 um 21:07 Uhr)
••••••••••
AI & KI NachrichtenOpenAI’s Dots: A First Look(02.10.2026 um 21:07 Uhr)
•
Intelligence View
⚡ tsecurity.de Intelligence

React Native Brownfield - Learning Guide

A beginner-friendly walkthrough of how to embed React Native inside existing native iOS and Android apps, using this demo workspace as a hands-on reference.…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

A beginner-friendly walkthrough of how to embed React Native inside existing native iOS and Android apps, using this demo workspace as a hands-on reference.









Table of Contents




  • What is "Brownfield"?

  • The Big Picture

  • Prerequisites

  • Project Structure


  • Understanding Each Project


    • RNApp -- Your React Native Code

    • AndroidApp -- The Native Android App

    • AppleApp -- The Native iOS App








  • Key Concepts Explained


    • What is an AAR?

    • What is an XCFramework?

    • What is the Brownfield CLI?

    • Debug vs Release

    • The Facade Pattern

    • Brownie -- Shared State Between Native and JS

    • JavaScript API






  • Step-by-Step: Android Integration


  • Step-by-Step: iOS Integration


  • Commands Cheat Sheet


  • Common Problems and Fixes


  • Monorepo Note


  • Learn More










What is "Brownfield"?



Imagine your company already has a native Android app written in Kotlin and a native iOS app written in Swift. They work fine, but now you want to add some new screens using React Native -- maybe a settings page, a chat feature, or a new onboarding flow.



You don't want to rewrite the entire app in React Native. You just want to add React Native into your existing app. That's brownfield integration.




















Term What it means
Greenfield Building a brand new app from scratch in React Native
Brownfield Adding React Native into an app that already exists


The @callstack/react-native-brownfield library makes this possible by packaging your React Native code into a single file that your native app can use -- just like any other native library. The native app does not need Node.js, npm, or any JavaScript tooling.




Trusted in production by Zalando, HelloFresh, and AutoZone. See the official site for more.







Version Compatibility




From the Introduction:





















React Native Version Brownfield Version
0.81.x, 0.82.x 2.x, 3.x
0.78.x ^1.2.0








The Big Picture



Here's what happens at a high level. You'll understand every piece of this by the end of this guide.





In plain English:




  1. You write your React Native app normally (screens, navigation, components, etc.)

  2. You run a CLI command that bundles everything -- your JS code, the Hermes engine, and all native dependencies -- into a single file

  3. Your native Android/iOS app adds that file as a dependency, initializes it, and shows the RN screens wherever it wants



The native app team doesn't need to know anything about React Native, npm, or JavaScript. They just call initialize() and render a view.









Prerequisites



Before you start, make sure you have these installed:











































Tool Version Why you need it
Node.js >= 20 Runs React Native tooling, Metro bundler, and the brownfield CLI
Android Studio Latest Builds the Android AAR and the native Android consumer app
Xcode Latest Builds the iOS XCFramework and the native iOS consumer app
CocoaPods Latest Manages iOS native dependencies (sudo gem install cocoapods)
JDK 17 Required by the Android Gradle build system
Watchman Recommended Makes the Metro bundler's file watching faster








Project Structure



This workspace has 3 projects:




RN_Brownfield_Apps/
│
├── RNApp/ # YOUR REACT NATIVE CODE
│ │ # This is a normal RN app that also gets
│ │ # packaged into AAR (Android) and XCFramework (iOS)
│ │
│ ├── index.js # Entry point -- registers the root component
│ ├── app.json # App name configuration
│ ├── package.json # Dependencies: RN 0.82.1, React 19.1, etc.
│ ├── BrownfieldStore.brownie.ts # Shared state definition (generates native types)
│ ├── metro.config.js # Metro bundler configuration
│ ├── babel.config.js # Babel configuration
│ ├── tsconfig.json # TypeScript configuration
│ │
│ ├── src/
│ │ ├── App.tsx # Root component with NavigationContainer
│ │ ├── HomeScreen.tsx # Your home screen
│ │ ├── navigation/ # React Navigation stack setup
│ │ └── components/ # Reusable UI components
│ │
│ ├── android/
│ │ ├── app/ # Standard RN Android app (for standalone dev)
│ │ ├── BrownfieldLib/ # IMPORTANT: This module becomes the AAR
│ │ │ └── build.gradle.kts # Brownfield plugin + Maven publishing config
│ │ ├── build.gradle # Root Gradle (adds brownfield plugin)
│ │ └── settings.gradle # Includes both :app and :BrownfieldLib
│ │
│ ├── ios/
│ │ ├── RNApp.xcworkspace # Open this in Xcode (not .xcodeproj)
│ │ ├── RNApp/ # Standard RN iOS app (for standalone dev)
│ │ ├── BrownfieldLib/ # IMPORTANT: This target becomes the XCFramework
│ │ │ └── BrownfieldLib.swift # Public interface that native apps import
│ │ └── Podfile # CocoaPods -- BrownfieldLib inherits all deps
│ │
│ └── __tests__/
│ └── App.test.tsx
│
├── AndroidApp/ # NATIVE ANDROID APP (the "consumer")
│ │ # A normal Kotlin + Compose app that imports
│ │ # the AAR to show React Native screens
│ │
│ ├── build.gradle.kts # Root Gradle configuration
│ ├── settings.gradle.kts # Adds mavenLocal() so Gradle finds the AAR
│ ├── gradle/
│ │ └── libs.versions.toml # Version catalog -- AAR coordinates defined here
│ └── app/
│ ├── build.gradle.kts # App build config with AAR dependency
│ └── src/
│ └── main/ # MainActivity, Compose UI, theme
│
└── AppleApp/ # NATIVE iOS APP (the "consumer")
│ # A normal SwiftUI app that imports the
│ # XCFramework to show React Native screens
│
├── prepareXCFrameworks.js # Script that copies XCFrameworks into project
├── Brownfield-Apple-App-Info.plist
├── Brownfield Apple App.xcodeproj/
└── Brownfield Apple App/
├── BrownfieldAppleApp.swift # App entry point -- initializes React Native
└── components/
├── ContentView.swift # Main view -- embeds ReactNativeView here
├── GreetingCard.swift # Native SwiftUI greeting card
├── MaterialCard.swift # Native material-style card
├── MessagesView.swift # Demo: sending messages between native and RN
└── Toast.swift # Toast notification component












Understanding Each Project






RNApp -- Your React Native Code




Ref: Quick Start

































What Value
React Native 0.82.1
React 19.1.1
Navigation React Navigation 7 (native-stack)
Brownfield lib @callstack/react-native-brownfield
State sharing @callstack/brownie


This is a completely normal React Native app. You can run it standalone with npx react-native run-android or npx react-native run-ios during development. The only extra thing it has is a BrownfieldLib module (Android) and target (iOS) that the CLI uses to package everything into a distributable artifact.



How it starts: index.js calls AppRegistry.registerComponent('RNApp', () => App). This is exactly how every React Native app works. The name 'RNApp' is what the native side uses to find and display your component.



What makes it "brownfield-ready":





  • Android: The android/BrownfieldLib/ folder is an Android Library module. The brownfield CLI packages it into an AAR file that contains your JS bundle, the Hermes engine, and all native dependencies baked in.


  • iOS: The ios/BrownfieldLib/ folder is an Xcode Framework target. The brownfield CLI packages it into an XCFramework with everything included.









AndroidApp -- The Native Android App




Ref: Android Integration Guide, Kotlin API





























What Value
Language Kotlin
UI Framework Jetpack Compose + Material 3
Min SDK 24
Compile SDK 36


This is a regular native Android app. It has zero React Native code in its source. It gets the React Native screens by importing the AAR file (published to the local Maven repository) as a Gradle dependency.



How it uses React Native (just 2 steps):





  1. Initialize -- In MainActivity.kt, one line boots up React Native:




ReactNativeHostManager.initialize(application) {
// Called when the JS bundle finishes loading
}








  1. Render -- Wherever you want a React Native screen, drop in a fragment:




AndroidFragment<ReactNativeFragment>(
arguments = Bundle().apply {
putString(ReactNativeFragmentArgNames.ARG_MODULE_NAME, "RNApp")
}
)






That's it. The native Android developer doesn't need to know anything about React Native internals.









AppleApp -- The Native iOS App




Ref: iOS Integration Guide, Swift API





















What Value
Language Swift
UI Framework SwiftUI


This is a regular native iOS app. It gets the React Native screens by embedding 3 XCFramework files into the Xcode project.



How it uses React Native (just 2 steps):





  1. Initialize -- In the app's init(), two lines boot up React Native:




ReactNativeBrownfield.shared.bundle = ReactNativeBundle
ReactNativeBrownfield.shared.startReactNative {
print("React Native has been loaded")
}







Important: You must set .bundle before calling .startReactNative. Otherwise you get a "No script URL provided" error.






  1. Render -- Wherever you want a React Native screen, use the SwiftUI view:




ReactNativeView(moduleName: "main")






That's it. The native iOS developer doesn't need to know anything about React Native internals.









Key Concepts Explained






What is an AAR?



AAR stands for Android Archive. Think of it like a ZIP file that contains:




  • Compiled Android code (.class files)

  • Resources (images, layouts, etc.)

  • Native libraries (.so files -- compiled C/C++ code)

  • A manifest file



It's the standard way to distribute Android libraries. When you add a library in build.gradle, Gradle downloads an AAR from a repository (like Maven Central or mavenLocal) and includes it in your app.



In our case, the AAR contains your entire React Native app -- JS bundle, Hermes runtime, and all native modules.




Ref: Android Integration - Step 7







What is an XCFramework?



XCFramework is Apple's format for distributing pre-compiled libraries. It can contain binaries for multiple platforms and CPU architectures (e.g., real iPhone arm64 + simulator arm64 + simulator x86_64) in a single bundle.



It replaced the older "fat framework" approach and is the modern way to share iOS libraries. You add it to your Xcode project by dragging it in.



In our case, you need to add 3 XCFrameworks to your iOS app:





  • BrownfieldLib.xcframework -- your React Native app code


  • hermesvm.xcframework -- the Hermes JavaScript engine


  • ReactBrownfield.xcframework -- the brownfield library itself




Ref: iOS Integration - Step 5, iOS Integration - Step 6







What is the Brownfield CLI?




Ref: Unified Brownfield CLI




The CLI is a command-line tool that comes with @callstack/react-native-brownfield. It automates everything you would otherwise have to do manually:




  • Bundle your JavaScript with the right settings

  • Compile native code for all CPU architectures (arm64, x86_64, etc.)

  • Compile Hermes bytecode for production

  • Embed all native dependencies into a single file

  • Handle debug and release variants



Without the CLI, you'd need to write and maintain complex build scripts yourself. With it, you just run:




# For Android: creates the AAR file
npx brownfield package:android --module-name :BrownfieldLib --variant release

# For Android: publishes the AAR to your local Maven repo (~/.m2/repository)
npx brownfield publish:android --module-name :BrownfieldLib

# For iOS: creates the XCFramework files
npx brownfield package:ios --scheme BrownfieldLib --configuration Release









Debug vs Release


























Mode Where JS code comes from What Hermes does When to use
Debug A local Metro dev server (npx react-native start) Interprets JS on the fly During development -- gives you hot reload and fast iteration
Release Pre-bundled inside the AAR/XCFramework Compiles JS to optimized bytecode For production -- no dev server needed, runs faster





The Facade Pattern




Ref: Guidelines




The official docs strongly recommend a pattern called the facade pattern. The idea is:




Your native app should never import React Native APIs directly. Instead, your brownfield artifact should expose a simple wrapper class (like ReactNativeHostManager) that hides all the RN internals.




Why? Because:




  • If something breaks, the stack trace points to your artifact, making debugging easier

  • The native team doesn't need to learn React Native APIs

  • If RN APIs change in a future version, you only update the wrapper -- the native app code stays the same



This project follows this pattern. Look at how clean the native app code is -- just ReactNativeHostManager.initialize() and ReactNativeFragment.






Brownie -- Shared State Between Native and JS




Ref: Brownie Overview, Codegen CLI




Sometimes your native app and React Native code need to share data -- for example, a counter value or a username. The @callstack/brownie library handles this with type-safe state synchronization.



Here's how it works:




  1. You define your shared state in a *.brownie.ts file (TypeScript)

  2. You run brownfield codegen to generate matching Swift types

  3. Both sides (JS and native) can read and write to this shared state, and changes sync automatically



In this project, BrownfieldStore.brownie.ts defines the shared state, and the codegen command generates the Swift types.




Note: Brownie currently supports iOS (Swift) only. Android support is coming soon.







JavaScript API




Ref: JavaScript Module API




From your React Native code, the library gives you these methods:




























Method What it does
ReactNativeBrownfield.popToNative(animated) Go back to the native screen that opened the RN screen
ReactNativeBrownfield.setNativeBackGestureAndButtonEnabled(enabled) Enable or disable the iOS swipe-back gesture and Android hardware back button
ReactNativeBrownfield.postMessage(data) Send data from RN to the native app
ReactNativeBrownfield.onMessage(callback) Listen for data sent from the native app to RN


Example of two-way communication:




import ReactNativeBrownfield from '@callstack/react-native-brownfield';

// Send a message to native
ReactNativeBrownfield.postMessage({ action: 'greet', name: 'Alice' });

// Listen for messages from native
const subscription = ReactNativeBrownfield.onMessage((event) => {
console.log('Native sent:', event.data);
});

// Clean up when done
subscription.remove();












Step-by-Step: Android Integration




Full reference: Android Integration Guide




Here's every step mapped to the actual files in this project:






Step 1: Create a library module




Ref: Create an Android Library Module




In Android Studio, go to File > New Module > Android Library. In this project, it's called BrownfieldLib and lives at RNApp/android/BrownfieldLib/.



It's registered in RNApp/android/settings.gradle:




include ':BrownfieldLib'









Step 2: Add the Gradle plugins




Ref: Set Up the AAR Gradle Plugin




First, add the plugin to the root RNApp/android/build.gradle:




classpath("com.callstack.react:brownfield-gradle-plugin:1.0.1-SNAPSHOT")






Then apply it in RNApp/android/BrownfieldLib/build.gradle.kts:




plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("com.callstack.react.brownfield") // The brownfield plugin
`maven-publish` // For publishing the AAR
id("com.facebook.react") // For autolinking
}









Step 3: Add React Native dependencies




Ref: Add React Native Dependencies





react {
autolinkLibrariesWithApp()
}

dependencies {
api("com.facebook.react:react-android:0.82.1")
api("com.facebook.react:hermes-android:0.82.1")
}






Important: The version numbers here must match the React Native version in your package.json. For RN >= 0.83, the Hermes package changes to com.facebook.hermes:hermes-android.






Step 4: Create the ReactNativeHostManager




Ref: Create React Native Host Manager




This is the "facade" class that hides all RN internals from the native consumer:




object ReactNativeHostManager {
fun initialize(application: Application, onJSBundleLoaded: OnJSBundleLoaded? = null) {
loadReactNative(application) // Required for RN >= 0.80.0
val packageList = PackageList(application).packages
ReactNativeBrownfield.initialize(application, packageList, onJSBundleLoaded)
}
}









Step 5: Configure Maven publishing




Ref: Configure Maven Publishing




The BrownfieldLib module publishes to mavenLocal() with coordinates com.rnapp:brownfieldlib:0.0.1-SNAPSHOT. A custom Gradle task strips internal RN dependencies from the POM file so the consumer doesn't try to download them separately (they're already baked into the AAR).






Step 6: Package and publish the AAR




Ref: Create the AAR, CLI docs





cd RNApp
npm run brownfield:package:android # Builds the AAR
npm run brownfield:publish:android # Publishes to ~/.m2/repository









Step 7: Add the AAR to your native app




Ref: Add the AAR to Your Android App




In AndroidApp/settings.gradle.kts, add mavenLocal() so Gradle can find the AAR:




dependencyResolutionManagement {
repositories {
mavenLocal()
}
}






In AndroidApp/gradle/libs.versions.toml, define the dependency coordinates:




[versions]
brownfieldlib = "0.0.1-SNAPSHOT"

[libraries]
brownfieldlib = { module = "com.rnapp:brownfieldlib", version.ref = "brownfieldlib" }






In AndroidApp/app/build.gradle.kts, add the dependency:




dependencies {
implementation(libs.brownfieldlib)
}









Step 8: Initialize and show the RN screen




Ref: Initialize React Native, Show the React Native UI




In MainActivity.kt, initialize React Native:




ReactNativeHostManager.initialize(application) {
Toast.makeText(this, "React Native has been loaded", Toast.LENGTH_LONG).show()
}






Then render it anywhere in your Compose UI:




AndroidFragment<ReactNativeFragment>(
arguments = Bundle().apply {
putString(ReactNativeFragmentArgNames.ARG_MODULE_NAME, "main")
}
)












Step-by-Step: iOS Integration




Full reference: iOS Integration Guide




Here's every step mapped to the actual files in this project:






Step 1: Create a Framework target in Xcode




Ref: Create a Framework Target




Open RNApp/ios/RNApp.xcworkspace in Xcode, then go to File > New > Target and choose Framework. In this project, it's called BrownfieldLib.



After creating it, set these build settings:

































Setting Value Why
Build Libraries for Distribution YES Creates a Swift module interface
User Script Sandboxing NO Allows the JS bundle script to write files
Skip Install NO Ensures Xcode produces the framework files
Enable Module Verifier NO Faster builds (skips verification)





Step 2: Update CocoaPods




Ref: Update CocoaPods




In RNApp/ios/Podfile, nest the framework target inside the app target so it inherits all dependencies:




target 'RNApp' do
config = use_native_modules!
use_react_native!(:path => config[:reactNativePath], ...)

target 'BrownfieldLib' do
inherit! :complete # Gets ALL pods from the parent target
end
end






Run pod install after making changes.






Step 3: Configure static linking




Ref: Static Linking Requirement




React Native Brownfield requires static linking to work. The Podfile handles this:




linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
use_frameworks! :linkage => linkage.to_sym
end






The CLI sets USE_FRAMEWORKS=static automatically when you run package:ios, so you don't need to worry about this during packaging.






Step 4: Add the bundle script




Ref: Add the Bundle Script




In Xcode, copy the "Bundle React Native code and images" build phase from the app target to your framework target. Add these input files:




  • $(SRCROOT)/.xcode.env.local

  • $(SRCROOT)/.xcode.env



This ensures the JS bundle gets compiled and included in the framework.






Step 5: Create the framework's public interface




Ref: Create the Framework's Public Interface




RNApp/ios/BrownfieldLib/BrownfieldLib.swift is a small but critical file:




@_exported import ReactBrownfield
@_exported import Brownie

public let ReactNativeBundle = Bundle(for: InternalClassForBundle.self)
class InternalClassForBundle {}






What this does:





  • @_exported import ReactBrownfield -- re-exports the brownfield library so the consumer can use its classes


  • ReactNativeBundle -- points to the framework's own bundle, which is where the JS code lives. The consumer app uses this to tell the brownfield library where to find the JavaScript.






Step 6: Package into XCFramework




Ref: Create the XCFramework, CLI docs





cd RNApp
npm run brownfield:package:ios






This creates 3 files in .brownfield/ios/package/:





  • BrownfieldLib.xcframework -- your React Native app


  • hermesvm.xcframework -- the Hermes JavaScript engine


  • ReactBrownfield.xcframework -- the brownfield library






Step 7: Add all 3 frameworks to your native iOS app




Ref: Add the Framework to Your iOS App




Drag all 3 .xcframework files into your Xcode project. In this project, the AppleApp/prepareXCFrameworks.js script automates this step.






Step 8: Initialize and show the RN screen




Ref: SwiftUI Integration, Swift API




In BrownfieldAppleApp.swift, initialize React Native when the app starts:




@main
struct BrownfieldAppleApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

init() {
ReactNativeBrownfield.shared.bundle = ReactNativeBundle // MUST come first
ReactNativeBrownfield.shared.startReactNative {
print("React Native has been loaded")
}
}

var body: some Scene {
WindowGroup {
ContentView()
}
}
}






Then in ContentView.swift, show the React Native screen:




ReactNativeView(moduleName: "main")
.clipShape(RoundedRectangle(cornerRadius: 16))
.background(Color(UIColor.systemBackground))












Commands Cheat Sheet






RNApp (your React Native project)








































Command What it does
npm run start Start the Metro dev server (needed for Debug mode)
npm run android Run the RN app standalone on Android (for development)
npm run ios Run the RN app standalone on iOS (for development)
npm run brownfield:package:android Package your RN code into an AAR file
npm run brownfield:publish:android Publish the AAR to local Maven (~/.m2/repository)
npm run brownfield:package:ios Package your RN code into XCFramework files
npm run codegen Generate Swift types from *.brownie.ts state definitions





AndroidApp (native consumer)
















Command What it does
npm run build Build the native app with the RNApp AAR





AppleApp (native consumer)
















Command What it does
npm run build Build the native app with the RNApp XCFramework








Common Problems and Fixes




Full list: Troubleshooting Guide







"Error: duplicate resources" during Android build



When: Building the AAR after upgrading from RN < 0.82 to >= 0.82.



Why: RN 0.82 changed the path where the JS bundle is written, causing a conflict with leftover files from the old path.



Fix: Delete the app/build/ directory and rebuild. This only needs to be done once.






"No script URL provided" on iOS Release builds



When: Running the iOS app in Release configuration.



Why: You forgot to set the bundle before starting React Native.



Fix: Make sure this line comes before startReactNative:




ReactNativeBrownfield.shared.bundle = ReactNativeBundle   // <-- this MUST be first
ReactNativeBrownfield.shared.startReactNative { ... }












Monorepo Note



This workspace comes from the react-native-brownfield monorepo by Callstack. Some config files reference parent directories:





  • RNApp/metro.config.js -- root set to ../..


  • RNApp/babel.config.js -- alias to ../../packages/react-native-brownfield/src


  • RNApp/tsconfig.json -- extends ../../tsconfig


  • package.json uses workspace:^ for brownfield dependencies



If you're using this standalone (not inside the monorepo), you'll need to:




  1. Replace workspace:^ with actual npm versions in package.json

  2. Update the ../../packages/... paths to point to node_modules/...









Learn More






Getting Started








CLI Reference








API Reference





  • JavaScript Module -- popToNative, postMessage, onMessage


  • Swift -- ReactNativeBrownfield, ReactNativeView, ReactNativeViewController


  • Objective-C -- same APIs for Obj-C


  • Kotlin -- ReactNativeBrownfield, ReactNativeFragment, createView


  • Java -- same APIs for Java






Brownie (Shared State)








Best Practices








Source Code



🔍 CTI & Forensik

Cyber Threat Intelligence & Forensik

ATT&CK-Navigator · IoC-Radar · Exploit-Belege
CTI Threat Relationship Graph
Akteure · Techniken · Beziehungen
2 Knoten · 1 Relationen
CVE / Incident Threat Actor Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten React Native Brownfield - Learning Guide

Thematisch verwandte Begriffe: React, Native, Brownfield, Learning · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag