new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo & React Native · all subjects

bare

68 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

All Expo tools work in any React Native app

All tools and services provided by Expo work great in any React Native app, whether or not it was originally built with Expo.

Incremental adoption phases for Expo

Expo can be adopted incrementally in four phases: Prerequisites (install expo package and Expo CLI), Quick wins (Expo SDK, expo-dev-client, native modules, upgrade helper), New workflows (app distribution, expo-updates), and New mindsets (Prebuild, Expo Router). Only Prerequisites is required for other phases; you can skip to tools most relevant to your goals.

Install expo package to unlock Expo capabilities

To use Expo capabilities in an existing React Native project, you must install the expo package. Installation steps are provided in the 'Install Expo modules' guide, with both automated and manual options.

Expo CLI is drop-in replacement for @react-native-community/cli

Migration to Expo CLI serves as a drop-in replacement for @react-native-community/cli and provides full compatibility with all Expo tools and services.

expo-dev-client provides Expo Go launcher in debug builds

The expo-dev-client package provides access to an Expo Go-style app launcher interface into debug app variants of existing React Native projects.

Expo Modules API for native modules

The Expo Modules API allows writing native modules using idiomatic Swift and Kotlin DSL, making it easier to build and maintain modules compared to traditional React Native native modules.

expo-updates for remote code updates

The expo-updates library can be installed and configured in existing React Native projects to manage remote updates to app code and enable PR previews.

Prebuild for maintaining native projects

Prebuild (Continuous Native Generation) allows simplifying native project maintenance by generating them on demand from configuration, rather than manually maintaining android and ios directories.

expo package has minimal footprint

The expo package has a small footprint since it includes only a minimal set of modules required in every app, with autolinking infrastructure and built-in Expo SDK libraries.

Native directories optional with Prebuild

Expo projects created with create-expo-app use Continuous Native Generation by default and do not contain android and ios native directories. When incrementally adopting Expo in existing React Native apps, you can keep native directories and use npx expo run:[android|ios] to compile locally.

CodePush retirement and EAS Update recommendation

CodePush will be retired in March 2025 and is incompatible with React Native's New Architecture. The recommendation is to switch to EAS Update for managing remote updates to app code. However, Expo tools including Expo SDK, Expo CLI, and EAS Build can be used in CodePush-enabled apps today.

Third-party native libraries with config plugins

Third-party libraries that require native project configuration or provide a config plugin can be installed in development builds of existing React Native projects.

React Navigation continues to work with Expo

React Navigation can continue to be used in projects adopting Expo tools. Expo Router is recommended as an alternative for its additional benefits, but React Navigation remains supported.

Android lifecycle listeners mechanism

On Android, ApplicationLifecycleDispatcher and ReactActivityHandler forward Application and Activity lifecycle events to registered listeners. Modules can provide ReactActivityLifecycleListener and ApplicationLifecycleListener implementations through a Package class to register callbacks.

iOS lifecycle listeners mechanism

On iOS, ExpoAppDelegate forwards AppDelegate calls to registered subscribers. Modules can provide an ExpoAppDelegateSubscriber implementation to register callbacks.

Android Application lifecycle integration

To integrate Application lifecycle listeners on Android, forward the onCreate() and onConfigurationChanged() calls from your Application class to ApplicationLifecycleDispatcher.

iOS AppDelegate integration with ExpoAppDelegateSubscriberManager

To integrate AppDelegate subscribers on iOS, forward the relevant calls to ExpoAppDelegateSubscriberManager in your existing AppDelegate implementation so that subscribers can respond to them.

iOS AppDelegate inheritance from ExpoAppDelegate

If your AppDelegate doesn't already extend another class, you can simplify the setup by inheriting from ExpoAppDelegate, which handles the forwarding automatically.

iOS AppDelegate supported lifecycle methods limitation

Not all UIApplicationDelegate methods that could cause significant side effects are supported. Refer to ExpoAppDelegate.swift in the Expo source for the full list of forwarded methods if you need to rely on a specific delegate.

Testing lifecycle listeners with expo-linking

To test if lifecycle callbacks are working correctly, install expo-linking, which uses lifecycle listeners to handle deep links. Add a listener for deep links using Linking.addEventListener('url', ({ url }) => {...}) and observe the console when opening a deep link.

Deep link testing example with expo-linking

import * as Linking from 'expo-linking'; import { useEffect } from 'react'; useEffect(() => { const listener = Linking.addEventListener('url', ({ url }) => { console.log('Received deep link:', url); }); return listener.remove; }, []);

Brownfield app definition

A brownfield app is an existing native app built using another technology (such as UIKit and Swift for iOS, or native Android) where the main entry point is not a React Native view. For example, an app built with UIKit and Swift that wants to use React Native for a single screen is considered a brownfield app. In contrast, greenfield apps are created using Expo or React Native from the start, or where React Native is the entry point with all other UI branching off from it.

Brownfield support status and limitations

Support for integrating Expo modules into existing native projects is in alpha. Expo is primarily built with greenfield apps in mind, but investment in brownfield scenarios is increasing. Not all Expo tools and services are compatible with existing native projects yet, and comprehensive documentation for brownfield integrations may not be available. Developers may need to adapt other related documentation to their brownfield context.

Expo tools and services brownfield compatibility matrix

The following compatibility status applies to brownfield apps: Expo SDK (extended standard library for React Native) - supported; Expo Modules API (build native extensions using Swift/Kotlin) - supported; Expo Router (file-based routing and navigation) - supported; Expo CLI (terminal tools to run and develop your app) - supported; Expo Dev Client (in-app developer tooling for Debug builds) - NOT supported; EAS Build (CI/CD service for Expo/React Native) - supported; EAS Submit (service that uploads your app to stores) - supported; EAS Update (instant updates of app JavaScript and assets) - supported.

Integrated approach for brownfield apps

In the integrated approach, React Native code lives inside the existing native project, allowing for tight coupling between React Native and native code. The existing Android or iOS native projects can be added to a subdirectory of the React Native project. If standard android and ios subdirectories cannot be used, a custom root folder for React Native code can be configured with a simple monorepo setup. Choose this approach if you need to frequently iterate on both native and React Native code together, have a single team managing both native and React Native development, or your project structure allows for adding a React Native project directly.

Android configuration changes for existing projects

For existing React Native projects, ensure the uiMode flag is present on MainActivity (and other activities) in AndroidManifest.xml: <activity android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode">

Android onConfigurationChanged implementation

Implement the onConfigurationChanged method in MainActivity.java to handle configuration changes. It should call super.onConfigurationChanged(newConfig), create an Intent with action "onConfigurationChanged", put the newConfig as an extra, and send it as a broadcast.

iOS Info.plist UIUserInterfaceStyle configuration

For existing React Native iOS projects, configure supported styles with the UIUserInterfaceStyle key in Info.plist. Use 'Automatic' to support both light and dark modes.

Expo tools work with React Native CLI projects

All Expo tools and services work great in any React Native app created with React Native CLI. This includes any part of the Expo SDK, `expo-dev-client`, and EAS Build, Submit, and Update. You can install `expo` in nearly any React Native project.

Privacy manifest for bare React Native projects

In an existing React Native project, you can include an iOS privacy manifest by creating a PrivacyInfo.xcprivacy file using Xcode and adding it to your iOS app target. Follow Apple's Privacy manifest files guide to create the file.

Remove Android permissions using tools:node attribute in AndroidManifest.xml

For React Native projects, you can modify AndroidManifest.xml to exclude specific permissions by adding the tools:node="remove" attribute to a <use-permission> tag. You must define the xmlns:tools attribute on the <manifest> element before using the tools:node attribute on permissions.

Android AndroidManifest.xml permission removal example

Example of removing a permission in AndroidManifest.xml: ```xml <manifest xmlns:tools="http://schemas.android.com/tools"> <uses-permission tools:node="remove" android:name="android.permission.ACCESS_FINE_LOCATION" /> </manifest> ```

Isolated approach overview

In the isolated approach, React Native code is developed and maintained separately from the native project. It is packaged as a native library using an AAR for Android or XCFramework for iOS, and integrated into the native app like any other dependency. This approach minimizes the impact of React Native on the existing native build process and is ideal when you have separate teams for native and React Native development. Native developers do not need Node.js, Yarn, or any React Native build tooling—they consume pre-built artifacts.

Create Expo project for brownfield

Run npx create-expo-app@latest my-project --template default@sdk-57 (npm), yarn create expo-app my-project --template default@sdk-57 (yarn), pnpm create expo-app my-project --template default@sdk-57 (pnpm), or bun create expo my-project --template default@sdk-57 (bun) to create a new Expo project. The project can be created in a separate repository or a monorepo and does not need to live inside the existing native app.

Install expo-brownfield library

Run npx expo install expo-brownfield (npm), yarn expo install expo-brownfield (yarn), pnpm expo install expo-brownfield (pnpm), or bun expo install expo-brownfield (bun) in the Expo project to install the expo-brownfield library, which provides tools to build React Native code as native libraries and integrate them into an existing native app.

Build Android AAR with expo-brownfield

Run npx expo-brownfield build:android (npm), yarn dlx expo-brownfield build:android (yarn), pnpm dlx expo-brownfield build:android (pnpm), or bunx expo-brownfield build:android (bun) from the Expo project directory. This builds the AAR and publishes it to a Maven repository. By default, it publishes to the local Maven repository (~/.m2), but it can be configured to publish to a remote repository. The produced artifact name is determined by config plugin settings, for example com.username.myproject:brownfield:1.0.0.

Build iOS XCFramework with expo-brownfield

Run npx expo-brownfield build:ios (npm), yarn dlx expo-brownfield build:ios (yarn), pnpm dlx expo-brownfield build:ios (pnpm), or bunx expo-brownfield build:ios (bun) from the Expo project directory. This builds the XCFramework artifacts by compiling the framework target for both device and simulator architectures, packaging them into XCFrameworks, and copying the Hermes engine framework. The output is placed in the ./artifacts directory and contains {TargetName}.xcframework (your Expo project as a native library) and hermesvm.xcframework (the Hermes JavaScript engine).

Ship iOS artifacts as Swift Package

Pass the --package [name] flag to npx expo-brownfield build:ios (or equivalent yarn/pnpm/bun command) to bundle the build output as a self-contained Swift Package instead of separate .xcframework directories. The flag accepts an optional name; if omitted, the package defaults to {TargetName}Artifacts. The resulting directory contains Package.swift and xcframeworks/ subdirectory with {TargetName}.xcframework, hermesvm.xcframework, React.xcframework, and ReactNativeDependencies.xcframework. Example: npx expo-brownfield build:ios --release --package MyAppPackage

Debug native targets with expo prebuild

Run npx expo prebuild (npm), yarn expo prebuild (yarn), pnpm expo prebuild (pnpm), or bun expo prebuild (bun) to generate the native projects with the brownfield library targets inside the android and ios directories. For Android, this generates a separate library module containing ReactNativeHostManager, BrownfieldActivity, ReactNativeFragment, ReactNativeViewFactory, and BrownfieldMessaging. For iOS, this generates a separate Xcode framework target containing ReactNativeHostManager, ReactNativeViewController, ReactNativeView (SwiftUI), BrownfieldMessaging, and ReactNativeDelegate.

Add Maven dependency for Android integration

In the app's build.gradle.kts, add the dependency: implementation("com.username.myproject:brownfield:1.0.0"). The group, artifact name, and version should match the config plugin settings. If the library was published to local Maven, add mavenLocal() to the repository configuration in settings.gradle.kts: dependencyResolutionManagement { repositories { google(); mavenCentral(); mavenLocal() } }

Android integration: show React Native screen

Create an activity that extends BrownfieldActivity and use the showReactNativeFragment() extension. Example: class ExpoActivity : BrownfieldActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState); showReactNativeFragment() } }. Add the activity to AndroidManifest.xml with a non-ActionBar theme and the following configChanges: keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode. Launch it with: startActivity(Intent(this, ExpoActivity::class.java)). BrownfieldActivity extends AppCompatActivity and handles forwarding configuration changes to Expo modules. The showReactNativeFragment() extension also sets up native back button handling automatically.

iOS integration: add XCFrameworks to project

Drag both XCFramework files ({TargetName}.xcframework and hermesvm.xcframework) into the Xcode project navigator. In the dialog, check 'Copy items if needed' and add them to the app target. Then in the target's General tab under Frameworks, Libraries, and Embedded Content, ensure both frameworks are set to Embed & Sign.

iOS integration: add Swift Package to project

When expo-brownfield build:ios produces a Swift Package (for example, ./artifacts/MyAppPackage-release), add it to the host app as a local dependency in Xcode by selecting the package directory. Xcode automatically links the bundled .xcframework files through the aggregate library product. To support both --debug and --release, point the host app at the matching package for each build configuration.

iOS initialization: initialize React Native host

Call ReactNativeHostManager.shared.initialize() early in the app's lifecycle, typically in AppDelegate. Example: import UIKit; import MyAppBrownfield; @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ReactNativeHostManager.shared.initialize(); return true } }

iOS integration: present React Native view with UIKit

Use ReactNativeViewController to present a React Native view. Example: let rnViewController = ReactNativeViewController(moduleName: "main"); navigationController?.pushViewController(rnViewController, animated: true). The ReactNativeViewController also accepts optional initialProps and launchOptions parameters: ReactNativeViewController(moduleName: "main", initialProps: ["userId": "123"], launchOptions: [:])

iOS integration: present React Native view with SwiftUI

Use ReactNativeView to present a React Native view in SwiftUI. Example: struct ContentView: View { @State private var showReactNative = false; var body: some View { Button("Open React Native") { showReactNative = true }.fullScreenCover(isPresented: $showReactNative) { ReactNativeView(moduleName: "main") } } }. The ReactNativeView accepts moduleName as required parameter.

Test React Native integration in development

Run npx expo start (npm), yarn expo start (yarn), pnpm expo start (pnpm), or bun expo start (bun) in the React Native directory to start the Metro bundler. Then build and run the native app from Android Studio or Xcode. When you navigate to the React Native screen, it will load from the Metro dev server with hot reloading support.

Test React Native integration in production

In release builds, the JavaScript bundle is embedded in the artifact (AAR or XCFramework), so the Metro server is not needed. Build the native app in Release configuration and verify the React Native screen loads correctly.

Setting up react-native-macos for macOS app support

To add macOS support to an app, follow the official 'Install React Native for macOS' guide from the react-native-macos documentation.

bare-expo Android Emulator command

Run the bare-expo app on an Android emulator with `pnpm android`. This command runs npm install if needed, builds the React Android binaries, generates an emulator, starts Metro, and opens the app in the emulator.

bare-expo iOS Simulator command

Run the bare-expo app on an iOS simulator with `pnpm ios`. This command automatically runs pod install, npm install, opens a simulator, clears and starts Metro, then opens the app in the simulator.

bare-expo E2E test on iOS Simulator

Run E2E tests on the iOS simulator with `pnpm test:ios`. This command performs the same setup as `pnpm ios` but additionally prepares the environment for E2E testing.

bare-expo open test command syntax

Open a specific test in the bare-expo test suite using `pnpm open <ios | android> <...Modules>`. The platform must be running already. This deep links into the test-suite app and runs the provided tests. Examples: `pnpm open ios Constants Crypto` or `pnpm open android Random`.

bare-expo nuke command

Reset the bare-expo development environment with `pnpm nuke`. This command deletes all generated files used for testing the setup scripts.

Bare Expo Android CMake precompiled header setup

The bare-expo Android app uses a CMakeLists.txt configuration that sets up a precompiled header (PCH) named appmodules_pch to optimize C++ compilation. The PCH is built from appmodules_pch_owner.cpp and includes the header file pch.h. The configuration applies ReactNative compile options to the PCH target and links it against common_flags, reactnative, jsi, folly_runtime, and fbjni dependencies.

Disable PCH timestamp in Bare Expo Android build

The CMake configuration disables PCH timestamps using the Clang compiler flag -Xclang;-fno-pch-timestamp to ensure precompiled headers are reproducible across builds, allowing ccache to reuse them effectively.

C++ standard enforcement for PCH consumers in Bare Expo

All libraries that reuse the appmodules_pch precompiled header are pinned to C++20 standard with CXX_EXTENSIONS OFF. This is necessary because Clang rejects a PCH built with a different C++ dialect, ensuring consistency between the PCH owner and all consuming targets.

PCH eligibility filter in Bare Expo CMake

The add_pch_if_eligible() function applies precompiled headers only to targets that exist, are not imported, and are not interface libraries. It skips application of PCH to imported targets and interface libraries.

Autolinked libraries PCH application in Bare Expo

If the AUTOLINKED_LIBRARIES variable is defined in the CMake configuration, each library in that list is processed through add_pch_if_eligible() to conditionally apply the appmodules_pch precompiled header.

Bare Expo app for SDK development

To contribute to the Expo SDK, use the Bare Expo app (located at https://github.com/expo/expo/tree/main/apps/bare-expo) for developing and testing changes, unless the changes are specific to the Expo Go app itself.

Give your agent this brain