Getting Started

This guide walks you through setting up Brownie from scratch and then integrating it per platform.

Prerequisites

Before starting, ensure you have:

Shared Setup

Step 1: Install Brownie

In your React Native app:

npm install @callstack/brownie

Step 2: Define Your Store

Create a store definition file with the .brownie.ts extension:

// src/stores/AppStore.brownie.ts
import type { BrownieStore } from '@callstack/brownie';

interface AppStore extends BrownieStore {
  counter: number;
}

declare module '@callstack/brownie' {
  interface BrownieStores {
    AppStore: AppStore;
  }
}

Import the store in your app entry point:

// App.tsx
import './stores/AppStore.brownie';

Step 3: Use the Store in React Native

import { useStore } from '@callstack/brownie';

function Counter() {
  const [counter, setState] = useStore('AppStore', (s) => s.counter);

  return (
    <View>
      <Text>Count: {counter}</Text>
      <Button
        title="Increment"
        onPress={() => setState((prev) => ({ counter: prev.counter + 1 }))}
      />
    </View>
  );
}

iOS

Step 4: Build XCFrameworks

The CLI generates Swift types and builds iOS artifacts:

npx brownfield package:ios --scheme YourScheme --configuration Release

This produces the following in ios/.brownfield/package/build/:

  • YourScheme.xcframework - Your React Native module
  • hermesvm.xcframework - JavaScript engine (or hermes.xcframework for RN < 0.82.0)
  • ReactBrownfield.xcframework - Brownfield integration
  • Brownie.xcframework - Shared state library (only when using the Brownie package)
  • BrownfieldNavigation.xcframework - Brownfield navigation integration (only when using the brownfield-navigation package)

The ios/.brownfield/build/ directory contains the build cache.

Info

The package:ios command automatically runs brownfield codegen to generate Swift types from your .brownie.ts files.

Step 5: Add Frameworks to Native App

  1. Open your native Xcode project
  2. Drag all XCFrameworks from ios/.brownfield/package/build/ into your project
  3. In target settings → GeneralFrameworks, Libraries, and Embedded Content, set all to Embed & Sign

Step 6: Register Store in Swift

In your app's entry point, register the store with initial state:

import Brownie
import ReactBrownfield
import SwiftUI

let initialState = AppStore(counter: 0)

@main
struct MyApp: App {
  init() {
    ReactNativeBrownfield.shared.startReactNative {
      print("React Native loaded")
    }

    AppStore.register(initialState)
  }

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

Step 7: Use the Store in SwiftUI

import Brownie
import SwiftUI

struct CounterView: View {
  @UseStore(\AppStore.counter) var counter

  var body: some View {
    VStack {
      Text("Count: \(Int(counter))")
      Button("Increment") {
        $counter.set { $0 + 1 }
      }
    }
  }
}

Android

Step 4: Add brownie configuration

Add the following block to your package.json:

"brownie": {
  "kotlin": "./android/BrownfieldLib/src/main/java/com/rnapp/brownfieldlib/Generated/",
  "kotlinPackageName": "com.rnapp.brownfieldlib"
},

Step 5: Add gson dependency

Add the gson dependency to your android module for brownfield:

api("com.google.code.gson:gson:2.13.1")

Otherwise, you can also add it to your native App:

implementation("com.google.code.gson:gson:2.13.1")

Step 6: Add AAR

The CLI generates Kotlin types and builds AAR:

npx brownfield package:android --module-name :YourModuleName --variant release

Once it succeeds, publish the AAR to local maven:

npx brownfield publish:android --module-name :YourModuleName
Info

The package:android command automatically runs brownfield codegen to generate Kotlin types from your .brownie.ts files.

Step 7: Register Store in Android

Register once during app startup:

import com.callstack.brownie.registerStoreIfNeeded
import com.rnapp.brownfieldlib.AppStore

registerStoreIfNeeded(
  storeName = AppStore.STORE_NAME
) {
  AppStore(counter = 0.0)
}

Step 8: Read and Update Store from Native UI

import com.callstack.brownie.StoreManager
import com.callstack.brownie.store
import com.rnapp.brownfieldlib.AppStore

val store = StoreManager.shared.store<AppStore>(AppStore.STORE_NAME)

// Read
val counter = store?.state?.counter ?: 0.0

// Update
store?.set { state ->
  state.copy(counter = state.counter + 1)
}

// Observe
val unsubscribe = store?.subscribe(
  selector = { state -> state.counter.toInt() },
  onChange = { updated -> println("Counter: $updated") }
)

Done!

State changes now sync automatically between React Native and native host code on both platforms.

Next Steps

Need React or React Native expertise you can count on?