# Expo — Bundle Drop Docs

> Set up Bundle Drop with Expo SDK 54–57, including managed builds, Expo Router, runtime versions, and strict fingerprint authority.

Canonical: https://bundledrop.app/docs/expo

### Expo Support

Bundle Drop supports Expo SDK 54–57 on iOS and Android, including managed/CNG projects and projects with committed native directories. The Expo config plugin owns the native integration, while the Metro wrapper preserves Expo's existing bundler behavior.

Both the old and new React Native architectures are supported.

### Install and Set Up

Install through Expo, then run the unified Bundle Drop setup:

**Terminal**

```bash
npx expo install @gfean/react-native-bundle-drop
npx bundle-drop login
npx bundle-drop doctor
```

`login` creates the project config, detects Expo, previews the changes, and asks before applying them. Expo setup registers `@gfean/react-native-bundle-drop` as a config plugin, wraps the existing Expo Metro config, and preserves unrelated plugins and Metro behavior.

To rerun setup explicitly:

**Terminal**

```bash
npx bundle-drop init --project-type expo
```

If native directories already exist, setup can preview a layered Expo prebuild so generated native integration stays aligned with `app.json` or `app.config.*`. Managed/CNG projects can keep `ios` and `android` absent until the next native build.

If guided setup cannot safely update a dynamic Expo configuration, follow the [manual Expo setup](/docs/manual-setup#expo-manual-setup). Do not patch generated `MainApplication` or `AppDelegate` files.

### Initialize with Expo Router

Call `BundleDrop.init` once in the root layout, before rendering the application tree:

**app/_layout.tsx**

```tsx
import { BundleDrop } from "@gfean/react-native-bundle-drop";
import { Stack } from "expo-router";

BundleDrop.init({
  enabled: !__DEV__,
  environment: __DEV__ ? "development" : "production",
  channelName: "develop",
  policy: "on-next-launch",
});

export default function RootLayout() {
  return <Stack />;
}
```

If your Expo app uses a conventional entrypoint instead of Expo Router, initialize before `AppRegistry.registerComponent`, as shown in [Installation](/docs/installation#initialize-bundle-drop-in-javascript).

### Development and Release Builds

Expo Go can run the app with Bundle Drop disabled because Expo Go does not contain the Bundle Drop native adapter. Debug and development-client builds also keep Metro in control. This makes normal development and debugging behave as expected, but those builds do not exercise Release cold-start OTA loading.

Use a non-Debug/Release native build to test OTA behavior:

**Terminal**

```bash
npx expo run:ios --configuration Release
npx expo run:android --variant release
```

For EAS Build, use your normal build profiles after setup:

**Terminal**

```bash
eas build --platform ios
eas build --platform android
```

Config plugin changes take effect in the next native generation or build. Rebuild whenever you add or remove native dependencies, change native plugins, switch JavaScript engines or architectures, or make another native compatibility change.

### Default Runtime Authority

By default, Bundle Drop uses literal runtime versions from `bundle.drop.config.js`:

**bundle.drop.config.js**

```js
module.exports = {
  projectType: "expo",
  runtimeVersion: {
    ios: "1.0.0",
    android: "1.0.0",
  },
  // serverUrl, org, and project are generated during login
};
```

This is the same runtime model used by bare React Native. Keep the value unchanged for compatible JavaScript and asset updates. When a native change affects one platform, bump that platform's value and rebuild it. Uploads in this mode do not require Expo build receipts or compare fingerprints.

### Bundle Drop Native Fingerprinting

Expo projects can use the same opt-in native fingerprint mode as bare React Native:

**bundle.drop.config.js**

```js
runtimeVersion: {
  compatibilitySource: "fingerprint",
},
```

Bundle Drop embeds the calculated identity in your native build and independently calculates it during upload. Matching identities allow compatible updates; a detected native change requires another binary. Fingerprinting is opt-in today and is intended to become the default in a future release.

You can also retain explicit `ios` and `android` versions alongside the fingerprint. This [hybrid approach](/docs/native-fingerprinting#hybrid-fingerprints-and-explicit-runtime-versions) combines automatic detection with your team's release boundary. See [Native Fingerprinting](/docs/native-fingerprinting) for setup, comparison commands, and coverage limitations.

For Continuous Native Generation, express native changes through Expo configuration, plugins, or [explicit fingerprint inputs](/docs/native-fingerprinting#coverage-and-custom-build-inputs); generated native directories are excluded. Keep the native build and upload on matching SDK versions, locked dependencies, and relevant build context. The [compatibility strategy guide](/resources/native-fingerprints-runtime-versions) compares manual, fingerprint, and hybrid control.

### Strict Expo Fingerprint Authority

If your release process instead delegates runtime authority to evaluated Expo configuration, use the separate Expo-owned mode:

**bundle.drop.config.js**

```js
module.exports = {
  projectType: "expo",
  runtimeVersion: { source: "expo" },
  // serverUrl, org, and project are generated during login
};
```

In strict mode, Bundle Drop resolves the Expo runtime policy and native fingerprint, records the identity of the built binary, and rejects an upload when its resolved identity does not match that build. This prevents an OTA bundle from being published against incompatible native inputs.

Strict mode also requires a clean Git worktree before a layered prebuild when native directories are committed. That check applies only to this opt-in authority mode; the default literal runtime flow does not require fingerprint receipts.

For a local Release build, build the exact source you intend to upload from and keep the generated receipt. For EAS Build, import the matching receipt before upload:

**Terminal**

```bash
npx bundle-drop eas-receipt ios --build-id <eas-build-id>
npx bundle-drop upload ios --channel develop --build-receipt <receipt-path>
```

Use strict mode when exact Expo build identity enforcement is part of your release policy. Use literal runtime versions when your team deliberately owns the compatibility boundary, or Bundle Drop native fingerprints when collected inputs should derive it, with optional explicit boundaries. Both Bundle Drop modes work across Expo and bare React Native.

The `source: "expo"` selector cannot be combined with Bundle Drop's `compatibilitySource`, fingerprint options, or explicit platform runtime values. Choose one authority mode for the native binary and its uploads.

### Upload Expo Updates

Expo uploads resolve app and runtime identity from the project configuration, so the normal commands do not need bare React Native version flags:

**Terminal**

```bash
npx bundle-drop upload ios --channel develop
npx bundle-drop upload android --channel develop
```

The installed native binary and uploaded bundle must still share the same runtime version. See [Runtime Version](/docs/runtime-version) before changing native dependencies or build configuration.

### Expo Updates Ownership

Bundle Drop and `expo-updates` both control which JavaScript bundle loads at native startup. Do not leave both active.

If the project currently uses `expo-updates`, run the explicit migration and review the proposed changes:

**Terminal**

```bash
npx bundle-drop init --project-type expo --migrate-expo-updates
```

Rebuild the native app after the migration. Existing EAS Build infrastructure remains usable; the conflict is specifically the runtime update layer inside the app.

### Verify the Integration

Run diagnostics after setup and after native-affecting configuration changes:

**Terminal**

```bash
npx bundle-drop doctor --project-type expo
```

Doctor verifies the config plugin, Metro wrapper, native integration when generated directories exist, runtime authority, and platform build identity.

### Related Docs

- [Installation](/docs/installation)
- [Manual Setup](/docs/manual-setup)
- [Runtime Version](/docs/runtime-version)
- [BundleDrop.init](/docs/init-update-manager)
- [Uploading](/docs/uploading)
- [CI/CD](/docs/ci-cd)
