ClaudeExpoMar 5, 2026, 2:30 PM

Expo brownfield: How to add Expo to your existing native app without a rewrite

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

claudeenmodel: claude-sonnet-4-20250514

Expo SDK 55 Introduces Isolated Brownfield Workflow for Existing Native Apps

Key Points

  • New isolated brownfield workflow packages Expo as native dependencies
  • Native developers avoid React Native build complexity
  • Built-in messaging API enables communication between native and Expo code

Summary

Expo SDK 55 introduces a new isolated brownfield workflow that allows developers to add React Native and Expo to existing native iOS/Android apps without requiring a complete rewrite. This approach packages Expo apps as precompiled native dependencies (XCFramework for iOS, AAR for Android), making integration feel like adding a standard library rather than adopting a new framework.

Key Points

  • Two brownfield approaches available: Integrated (direct installation) and isolated (precompiled artifacts)
  • Isolated approach benefits: Native developers don't need Node.js setup or React Native build dependencies
  • Built with Continuous Native Generation (CNG): No manual Xcode/Gradle project management required
  • New expo-brownfield package: Provides build commands and messaging API for communication
  • Bi-directional messaging: PostMessage-like API for communication between native host and embedded Expo app

Implementation Steps

  1. Install expo-brownfield package
  2. Build platform-specific artifacts:
    • iOS: npx expo-brownfield build:ios (generates XCFramework)
    • Android: npx expo-brownfield build:android (publishes AAR to Maven)
  3. Integrate artifacts into native project as standard dependencies
  4. Initialize React Native host from native code

Limitations

  • Single embedded app per native app (due to React Native runtime conflicts)
  • Build performance: Slow artifact generation, no precompiled binary reuse
  • Library compatibility: Some Expo libraries may have limited brownfield support
  • Binary distribution: Large artifacts typically require Git LFS or artifact repositories

Full Translation

Translations

A translation section that keeps the flow of the original article.

claudejamodel: claude-sonnet-4-20250514

Expo brownfield: 既存のネイティブアプリを書き換えることなくExpoを追加する方法

既存のネイティブiOS/Androidアプリを書き換えることなくExpoを追加する方法を学びます。Expo SDK 55の新しい分離型brownfieldワークフローについて説明します。

概要

本番環境で稼働している既存のネイティブアプリがあるとします。それは長年にわたるプラットフォーム固有の決定の結果であり、完全に置き換えたり一度にすべてを変更したりするものではありません。同時に、そのアプリ内で新機能をReact Nativeを使用して構築したい場合があります(例えば、プラットフォーム間でロジックを共有するため)が、それを可能にするためにアプリの残りの部分を書き換える必要はありません。

これがbrownfieldの意味するところです:メインエントリーポイントがReact Nativeではない既存のネイティブアプリにReact NativeとExpoを追加することです。問題はReact Nativeを追加できるかどうかではなく、全か無かのアーキテクチャ決定ではなく、段階的で自己完結型で、より広いチームに破壊的でない方法でそれを導入する方法です。

SDK 55での新機能

SDK 55では、非React Nativeネイティブアプリにexpoの画面とコンポーネントを統合できる分離型brownfieldワークフローを導入しました!これは、プリコンパイルされたExpoアプリをネイティブ依存関係として埋め込むことで実現され、新しいフレームワークを採用するというよりもライブラリを追加するような体験に近くなります。

既存ネイティブアプリでのExpoの位置づけ

チームが既存のネイティブアプリにReact Nativeを導入することを検討する理由はさまざまです:

  • 段階的採用: コードベース全体を一度にコミットすることなく、アプリの一部でExpoの使用を開始し、時間をかけてその使用を拡大する可能性を残す
  • 選択的使用: 自己完結型の機能エリアやサブアプリ(例:FacebookアプリのMarketplace)など、製品の特定の部分を構築するためにReact Nativeを選択的に使用し、アプリの残りの部分は変更しない
  • ビジネスニーズへの対応: 買収後に既存のReact Nativeアプリをネイティブ製品に統合するなど、技術的なものではなくビジネスニーズに対応する

これらのシナリオ全体で要件は同じです:React Nativeは、痛みを伴う書き換えや既存アプリの不自然な歪曲なしに、すでに存在するアプリに適合する必要があります。

Expoのbrownfieldアプローチ

Expoのbrownfieldアプローチは、コードベースとそれに取り組むチームへの破壊を最小限に抑えてReact Nativeの使用を可能にすることを目的としています。Expoは、サポートされ保守可能な方法でネイティブアプリにReact Nativeを埋め込むための明示的なAPIとパターンを提供します。

Expoでのbrownfieldアプローチ

Expoは現在、既存のネイティブアプリにReact Nativeを追加する2つの異なる方法を文書化しています:

統合アプローチ

統合アプローチは、React NativeとExpoを直接ネイティブアプリにインストールします。しかし、React Nativeをネイティブアプリに埋め込むことは、チーム全体が対応しなければならない二次的なランタイム、ビルドシステム、開発環境を導入するため、典型的なライブラリを追加するよりも複雑です。

分離アプローチ(SDK 55の新機能)

代替として、SDK 55で分離アプローチを導入しました。これは、React Nativeコードをネイティブライブラリ(AndroidのAARまたはiOSのXCFramework)としてパッケージ化し、他の依存関係と同様にネイティブアプリに統合できます。

ネイティブ開発者は、Node.js環境をセットアップしたり、React Nativeのビルド依存関係を扱ったりする必要がありません。彼らは単にプリビルドされたアーティファクトを消費するだけで、複雑さはReact Nativeチーム内に含まれ、組織の残りの部分には見えません。

分離アプローチの実践

分離アプローチでは、Expoアプリは事前にビルドされ、ネイティブバイナリアーティファクトとして配布されます。Expoの観点から見ると、これは単なるExpoプロジェクトです。

セットアップと使用

分離アプローチは新しいexpo-brownfieldパッケージで実装されています:

# パッケージをインストール
npx expo install expo-brownfield

# iOS XCFrameworkをビルド
npx expo-brownfield build:ios

# Android AARをビルド
npx expo-brownfield build:android

iOS実装

iOSでは、プロジェクト内にartifactsディレクトリが作成され、ネイティブフレームワークとしてコンパイルされたアプリの出力が含まれます:

├── app/
├── artifacts/
│   ├── hermesvm.xcframework/
│   └── expohelloworldbrownfield.xcframework/
├── assets/
├── ios/
├── node_modules/
├── .gitignore
└── app.json

生成された.xcframeworkをXcodeプロジェクトにドラッグします。そこから、他の依存関係と並んで表示され、アプリにリンクされます。

Android実装

Androidでは、出力は.aarとしてパッケージ化されますが、配布方法が少し異なります。ファイルをネイティブプロジェクトにコピーするのではなく、ビルドはローカルMavenディレクトリ(通常は~/.m2)にアーティファクトを公開します。

// settings.gradleまたはbuild.gradle
dependencyResolutionManagement {
    repositories {
        mavenLocal()
        google()
        mavenCentral()
    }
}

埋め込みアプリは、それをホストするモジュールで通常の依存関係として追加できます:

dependencies {
    implementation("com.example.helloworld:brownfield:1.0.0")
}

テーマ設定

ホストアクティビティは、React Nativeが期待する属性を提供するテーマを使用する必要があります:

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:label="@string/app_name"
-   android:theme="@style/Theme.Helloworld">
+   android:theme="@style/Theme.AppCompat.Light.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

ネイティブと埋め込みアプリ間の通信

分離アプローチには、ネイティブホストと埋め込みExpoアプリ間でメッセージを渡すための組み込み通信APIが含まれています。このAPIは、埋め込みアプリの内部モジュールへの直接アクセスを必要とせずに、イベントとデータを交換するための構造化された双方向チャネルを提供します。

iOSでのメッセージ送信

// iOS
import ExpoBrownfield

BrownfieldMessaging.sendMessage([
    "type": "MyIOSMessage",
    "timestamp": Date().timeIntervalSince1970,
    "data": ["platform": "ios"]
])

React Nativeでのメッセージ受信

import * as Brownfield, { type MessageEvent } from 'expo-brownfield';
import { useEffect } from 'react';

function MyComponent() {
    useEffect(() => {
        const handleMessage = (event: MessageEvent) => {
            console.log('Received message:', event);
        };
        
        Brownfield.addMessageListener(handleMessage);
        
        return () => {
            Brownfield.removeMessageListener(handleMessage);
        };
    }, []);
}

制限事項とトレードオフ

分離アプローチの制限事項

  • 単一埋め込みアプリ: ネイティブアプリには1つの埋め込みアプリのみを含めることができます。各埋め込みアプリには独自のReact Nativeランタイムのコピーが含まれているため、複数のフレームワークを含めるとビルド時にクラス名の衝突が発生します

  • 共有ランタイム: 複数の論理的な体験は単一の埋め込みランタイムを共有する必要があります

  • 自己完結性: 埋め込みアプリは意図的に自己完結型です。埋め込みアプリ外のコードは、Expoモジュールやその他の内部実装の詳細に直接アクセスできません

  • ビルド時のトレードオフ: フレームワークやAARのビルドは遅く、プリコンパイルされたReact Nativeバイナリはこのコンテキストでは再利用できません

ライブラリ互換性の考慮事項

一部のライブラリは、brownfieldセットアップで使用する場合、期待通りに動作しない場合や、限定的なドキュメントしかない場合があります。これには特定のExpoライブラリも含まれます。

例えば、expo-updatesパッケージは統合および分離brownfieldアプローチの両方で利用可能ですが、現在はネイティブアプリごとに単一のExpoプロジェクトと単一の更新URLを想定しています。

Expo brownfield: How to add Expo to your existing native app without a rewrite | Expo | DocsDigest