--- title: "Expo Integration Guide" slug: "expo-integration-guide-react-native-engage" updated: 2026-09-03T14:22:00Z published: 2026-09-03T14:22:00Z canonical: "docs.mapp.com/expo-integration-guide-react-native-engage" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.mapp.com/llms.txt > Use this file to discover all available pages before exploring further. # Expo Integration Guide The Mapp Engage React Native plugin supports Expo through a config plugin. Expo apps get the plugin's native setup — permissions, services, capabilities — applied automatically on every prebuild, without manual native code changes. > [!WARNING] > Warning > > Expo Go does not support this plugin, or any SDK with native code. You need a development build (`expo-dev-client`) or EAS Build. This is the most common source of confusion when setting up Expo, so it is worth confirming before anything else. --- ## Why a config plugin Expo regenerates the native `android` and `ios` folders on every clean prebuild, which wipes any manual native setup. The config plugin reapplies the Mapp Engage native setup automatically each time you prebuild, so your configuration survives rebuilds — both local and via EAS Build. --- ## Install ```bash npx expo install react-native-mapp-plugin expo-dev-client ``` --- ## Configure app.json Add the plugin to the `plugins` array with the options for your project: ```json {  "expo": {    "name": "Mapp app",    "slug": "mapp-app",    "newArchEnabled": true,    "ios": {      "bundleIdentifier": "com.example.mappapp"    },    "android": {      "package": "com.example.mappapp",      "googleServicesFile": "./google-services.json"    },    "plugins": [      [        "react-native-mapp-plugin",        {          "android": {            "enableGeofencing": false,            "pushHandling": "mapp"          },          "ios": {            "appId": "MAPP_APP_ID",            "dmcSystemId": 123,            "sdkKey": "MAPP_SDK_KEY",            "isEu": true,            "inAppServerUrl": "MAPP_INAPP_SERVER_URL",            "openLandingPageInsideApp": false,            "customFields": ["customString", "customNumber", "customDate"],            "mediaTimeout": 5,            "enableGeofencing": false          }        }      ]    ]  } } ``` ### Android options | Option | Description | | --- | --- | | `enableGeofencing` | Adds fine and background location permissions when `true`. | | `pushHandling` | `"mapp"` (default) or `"custom"`. See Android push ownership below. | ### iOS options | Option | Description | | --- | --- | | `appId` | Mapp Engage application ID. | | `dmcSystemId` | Mapp DMC system ID. | | `sdkKey` | Mapp SDK key. | | `isEu` | Whether the application uses Mapp's EU environment. | | `inAppServerUrl` | In-app message server URL. | | `openLandingPageInsideApp` | Opens push landing pages inside the app instead of the system browser. | | `customFields` | Custom attribute keys the native SDK should expose, by type. | | `mediaTimeout` | Rich Push media download timeout, in seconds. | | `enableGeofencing` | Adds location usage descriptions and capabilities when `true`. | The Android package must match the Firebase Android application in `google-services.json`. For iOS, configure an APNs-enabled App ID, a matching bundle identifier, and Apple/EAS signing credentials. > [!NOTE] > Note > > Values in `app.json` and native resources are public application configuration. Do not put service-account keys or signing secrets there. The Firebase file is your own configuration. Point Expo's built-in `expo.android.googleServicesFile` field at it — this plugin does not copy, generate, or modify `google-services.json`. --- ## Build and run ```bash npx expo prebuild --clean npx expo run:android npx expo run:ios # Cloud builds, after configuring EAS eas build --profile development --platform all eas build --profile preview --platform all eas build --profile production --platform all ``` Changing plugin options or native dependencies requires a new binary. JavaScript-only changes can use EAS Update. ### Initialization Register event listeners at application startup, then initialize Mapp. Since 2.0.0, `engage()` returns a promise — await it before calling APIs that depend on the native Mapp singleton. On iOS, the generated `AppoxeeConfig.plist` is the credential source of truth; the `engage` arguments remain relevant to Android. ```javascript import { Mapp, MappEventEmitter } from 'react-native-mapp-plugin'; const events = new MappEventEmitter(); const subscription = events.addListener('com.mapp.deep_link_received', event => {  // Route the deep link. }); await Mapp.engage('ANDROID_SDK_KEY', 'FCM_PROJECT_ID', 'EMC', 'APP_ID', 'TENANT_ID'); ``` --- ## Android push ownership `pushHandling: "mapp"` is the default. It requires `expo.android.googleServicesFile` and keeps `com.reactlibrary.MessageService` as the sole Mapp FCM callback owner. The config plugin removes the Mapp SDK v7 service from the merged app manifest. Use `pushHandling: "custom"` when another integration — such as your own `FirebaseMessagingService` — owns callbacks. The plugin then removes both its own service and the Mapp SDK service, so your service owns callbacks. From native Android code, forward through `MappPushHelper`: ```java @Override public void onMessageReceived(RemoteMessage message) {  if (!MappPushHelper.handleMessage(getApplication(), message)) {    // Handle non-Mapp messages here.  } } @Override public void onNewToken(String token) {  MappPushHelper.handleNewToken(getApplication(), token); } ``` See the [MappPushHelper reference](/docs/en/mapppushhelper-reference-react-native-engage) for the full method list. > [!NOTE] > Note > > `Mapp.setRemoteMessage(...)` remains available when JavaScript is guaranteed to be alive, but the native helper is required for reliable background and terminated delivery. If you also use `expo-notifications`, use custom ownership with explicit native forwarding — a successful manifest merge alone does not forward payloads. --- ## Geofencing Set `enableGeofencing` on each platform that needs it. Android then adds fine and background location permissions, and `Mapp.requestGeofenceLocationPermission()` requests foreground permission before background permission. On iOS, also supply non-empty `locationWhenInUsePermission` and `locationAlwaysPermission` messages. > [!NOTE] > Note > > Only request location access when your user-facing feature and store policy justify it. --- ## iOS Rich Push The config plugin creates a `MappNotificationService` Notification Service Extension with the bundle identifier `<expo.ios.bundleIdentifier>.mappnotificationservice`. The extension reads the public Mapp `ios_apx_media` payload key, downloads the media, and attaches it to the notification. It requires no additional CocoaPod or React Native code. > [!WARNING] > Warning > > The extension must use the same iOS deployment target as your main app, or it loses connection to it. It is set to iOS 15+ by default. If you raise your app's deployment target, verify the extension target matches. > [!NOTE] > Note > > The extension shares an App Group with the main app to connect the two for Rich Push. This is required — a project without a shared App Group cannot deliver Rich Push media. The extension is also declared in Expo's experimental EAS app-extension metadata, so EAS can prepare its signing credentials. Regenerate the iOS project after changing the application bundle identifier. --- ## Tested compatibility | Component | Tested baseline | | --- | --- | | Expo SDK | 57 | | React Native | 0.86 (Expo SDK 57) | | New Architecture | Enabled | | Android min / compile / target SDK | 24 / 36 / 36 | | Android Gradle Plugin / Kotlin / JDK | 8.12 / 2.1.20 / 21 | | iOS deployment target | 16.4 (library minimum: 15.1) | | Mapp Android SDK | 7.1.2 | | Mapp iOS SDKs | Vendored xcframeworks in this package | Mapp Engage Android 7.1.2 currently publishes Android dependencies newer than the Expo SDK 57 toolchain can consume. This release exports bounded compatibility constraints for AndroidX Core, WorkManager, Lifecycle, Play Services Location, and Kotlin stdlib — see [Breaking Changes in 2.0.0](/docs/en/breaking-changes-2-0-0-react-native-engage) for the exact versions. --- ## React Native CLI Not using Expo? Install the plugin directly; autolinking discovers the Android package and CocoaPod automatically. ```bash npm install react-native-mapp-plugin cd ios && pod install ``` Do not run `react-native link`, edit `settings.gradle`, or add the pod manually. See [Get Started](/docs/en/getting-started-react-native-engage) for the full setup.