# customerio-reactnative

> Official Customer.io SDK using React Native. Track customers and send messages to your iOS/Android apps.

Latest version **7.2.1** (published 2026-10-29) · MIT license · 0 weekly downloads

## Install

```sh
npm install customerio-reactnative
pnpm add customerio-reactnative
yarn add customerio-reactnative
bun add customerio-reactnative
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 7.2.1 |
| Published | 2026-10-29 |
| First published | 2022-06-23 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=20.0.0 |
| Dependencies | 0 |
| Unpacked size | 802.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | yes |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 40 |
| Author | CustomerIO Team |
| Maintainers | ami-ci |
| Keywords | react-native, ios, android |

## Links

- npm: https://www.npmjs.com/package/customerio-reactnative
- Repository: https://github.com/customerio/customerio-reactnative
- Homepage: https://github.com/customerio/customerio-reactnative#readme
- Issues: https://github.com/customerio/customerio-reactnative/issues
- npm.io page: https://npm.io/package/customerio-reactnative

## Alternatives

- [@sindresorhus/slugify](https://npm.io/package/@sindresorhus/slugify.md) — 3.7M weekly downloads
- [solid-js](https://npm.io/package/solid-js.md) — 2.7M weekly downloads
- [expo-glass-effect](https://npm.io/package/expo-glass-effect.md) — 2.5M weekly downloads
- [nanoassert](https://npm.io/package/nanoassert.md) — 780.8K weekly downloads
- [@ffmpeg/ffmpeg](https://npm.io/package/@ffmpeg/ffmpeg.md) — 529.5K weekly downloads

## Recent versions

- 7.2.1 (latest) — 2026-10-29
- 0.0.0-test-oidc (test-oidc) — 2025-11-20
- 2.0.0-beta.1 (next) — 2022-12-30
- 1.0.0-alpha.1 (alpha) — 2022-06-23
- 7.2.0 — 2026-10-27
- 7.1.0 — 2026-10-27
- 7.0.0 — 2026-10-26
- 6.16.1 — 2026-10-23
- 6.16.0 — 2026-10-23
- 6.15.1 — 2026-10-23
- 6.15.0 — 2026-10-22
- 6.14.0 — 2026-10-21
- 6.13.1 — 2026-09-30
- 6.13.0 — 2026-09-25
- 6.12.0 — 2026-09-17
- … 119 more at https://npm.io/package/customerio-reactnative/versions

## README

<p align="center">
  <a href="https://customer.io">
    <img src="https://avatars.githubusercontent.com/u/1152079?s=200&v=4" height="60">
  </a>
</p>

[![npm version](https://img.shields.io/npm/v/customerio-reactnative.svg)](https://www.npmjs.com/package/customerio-reactnative)
[![npm downloads](https://img.shields.io/npm/dm/customerio-reactnative)](https://www.npmjs.com/package/customerio-reactnative)
![min React Native version is 0.77](https://img.shields.io/badge/min%20React%20Native-0.77-purple)
![min Android SDK version is 24](https://img.shields.io/badge/min%20Android%20SDK-24-green)
![min iOS version is 15.1](https://img.shields.io/badge/min%20iOS%20version-15.1-blue)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)

# Customer.io React Native SDK

The official Customer.io SDK for React Native enables you to integrate mobile messaging features—like in-app messaging and push notifications—into your app. These features can be triggered by events sent through the SDK and configured using your Customer.io campaigns.

> 📖 Full documentation: [customer.io/docs/sdk/react-native](https://customer.io/docs/sdk/react-native/)  
> 🧪 Example apps: [see the `/example` directory](/example)

---

## Requirements

| | Minimum | Where it comes from |
|---|---|---|
| React Native | 0.77, New Architecture enabled | The SDK supports only the New Architecture (since 6.0.0). Its Android module applies the Compose compiler plugin with your app's Kotlin version, which needs Kotlin 2.0 or later; React Native 0.77 is the first release whose template uses Kotlin 2.0. |
| iOS | 15.1 | React Native 0.76 and later require iOS 15.1. |
| Android | API 24 (Android 7.0) | React Native 0.76 and later require API 24. |
| Android build | `compileSdkVersion` 36, Android Gradle Plugin 8.9.1 | The native Android SDK depends on AndroidX Core 1.17, which requires both. |

The native Customer.io SDKs (5.0.0) require iOS 15 and API 24, which these React Native minimums
already cover.

Apps created from the React Native 0.81 template or later meet the Android build requirement as
generated. CI builds Android template apps with React Native 0.81 and 0.88, and iOS with the example
app (React Native 0.83) and the scene E2E host (React Native 0.88). On React Native 0.77 to 0.80,
update the app's Android build:

- Set `compileSdkVersion = 36` and `buildToolsVersion = "36.0.0"` in `android/build.gradle`.
- On 0.77 to 0.79, also raise the Android Gradle Plugin to 8.9.1 or later
  (`classpath("com.android.tools.build:gradle:8.9.1")`), which needs Gradle 8.11.1 or later in
  `android/gradle/wrapper/gradle-wrapper.properties`.

Expo apps regenerate `android/` on prebuild and take the Android Gradle Plugin and Gradle versions
from the Expo SDK; Expo SDK 54 (React Native 0.81) and later meet the Android build requirement.

---

## Installation

```bash
npm install customerio-reactnative
```

If a CocoaPods build reports that a generated dependency target is below the deployment range
supported by Xcode, follow the [deployment-target normalization guide](docs/cocoapods-deployment-target-normalization.md).

---

## SDK Initialization

Here’s a simplified example showing how to initialize the SDK to enable most mobile features:

```ts
import {
  CustomerIO,
  CioConfig,
  CioLogLevel,
  CioRegion,
} from 'customerio-reactnative';

useEffect(() => {
  const config: CioConfig = {
    cdpApiKey: 'your-cdp-api-key', // Required
    migrationSiteId: 'your-site-id', // Optional, for migrating from older SDKs
    region: CioRegion.US, // Or CioRegion.EU
    logLevel: CioLogLevel.debug,
    trackApplicationLifecycleEvents: true,
    inApp: {
      siteId: 'your-site-id', // Optional with a pk_ key
    },
    push: {
      android: {
        pushClickBehavior: 'ActivityPreventRestart', // Optional
      },
    },
  };

  CustomerIO.initialize(config);
}, []);
```

> 🔑 For help finding your credentials, check out the [Quick Start Guide](https://customer.io/docs/sdk/react-native/quick-start-guide/#step-1).

---

## 📲 Push Notifications

This SDK supports [rich push notifications](https://customer.io/docs/sdk/react-native/rich-push/) using Firebase (for Android) and either Firebase or APNs (for iOS). Follow our [push setup guide](https://customer.io/docs/sdk/react-native/push/) to configure your project for push.

On iOS, a React Native 0.88+ `UIScene` host using the acknowledged handler must declare that ownership before React Native starts. This API is not used on Android or by AppDelegate-only hosts. Call this first in `scene(_:willConnectTo:options:)` so cold destinations wait for the JavaScript handler instead of entering the legacy `Linking` path:

```swift
NativeCustomerIO.configureAcknowledgedSceneDeepLinkRouting()
```

Register the app's Customer.io deep-link handler before calling `CustomerIO.initialize`. Return
`true` after routing a URL. Return `false` to let the native SDK try the host AppDelegate, then pass
a host-owned custom scheme to React Native `Linking` or open other URLs through the system. If your
handler might decline an app-owned custom scheme, keep a `Linking` URL listener registered for that
fallback. A thrown error, rejected promise, missing handler, or handler timeout follows the same
native fallback. Cold URLs wait up to ten seconds for registration and are replayed once the handler
is ready. After delivery, the handler has ten seconds to settle. If it takes longer, native fallback
runs; a handler that later routes the URL can cause a second navigation because its acknowledgement
cannot cancel the fallback.

```typescript
const subscription = CustomerIO.setDeepLinkHandler(async (url) => {
  if (!canRouteInApp(url)) {
    return false;
  }

  await routeInApp(url);
  return true;
});

CustomerIO.initialize(config);
```

Remove the returned subscription when the routing owner is torn down. If no replacement handler
registers, later destinations wait for the readiness timeout and then use the native fallback.

The existing `Linking` path remains available for backward compatibility. Keep
`NativeCustomerIO.configureSceneDeepLinkRouting()` in your SceneDelegate, then signal readiness
after registering the listener:

```typescript
const subscription = Linking.addEventListener('url', ({ url }) => {
  // Route the URL in your app.
});
CustomerIO.setDeepLinkRoutingReady();
```

Cold destinations wait up to ten seconds for this readiness signal. If it does not arrive, the SDK
tries the host AppDelegate, passes a host-owned custom scheme to React Native `Linking`, or opens
other URLs through the system.

This older path cannot acknowledge whether JavaScript handled the URL, so the SDK cannot safely fall back after it publishes a `Linking` event. Prefer `setDeepLinkHandler` for new UIScene integrations. Older React Native versions and AppDelegate-only hosts keep their existing deep-link integration.

Expo apps keep the `Linking` path. With config-plugin auto-initialization, register the app's
`Linking` listener, then call `CustomerIO.setDeepLinkRoutingReady()` after the router is ready. The
Expo plugin configures its native scene lifecycle automatically, so Expo app code does not call a
native configuration method. Apps using JavaScript initialization register the router before
`CustomerIO.initialize`; initialization marks Linking ready automatically.

This integration applies after the host has adopted React Native's UIScene lifecycle; the plugin does not replace React Native's root application lifecycle.

This release's compatibility scope is one simultaneous window scene. Multiple simultaneous React Native window scenes are not supported. React Native's URL notification is process-wide, so SDK-published destinations may reach every connected React Native instance rather than one selected window.

### iOS FCM with React Native Firebase 26.1 or later

React Native Firebase 26.1 and later adds Firebase through Swift Package Manager by default. The `fcm` subspec adds Firebase through CocoaPods as well, so the build fails with two copies of Firebase. Use the `fcm-core` subspec in its place. It adds no Firebase of its own and uses the FirebaseMessaging that React Native Firebase brings in:

```ruby
# React Native Firebase's Swift Package Manager setup needs dynamic frameworks.
use_frameworks! :linkage => :dynamic

target 'YourApp' do
  config = use_native_modules!
  pod 'customerio-reactnative/fcm-core', :path => '../node_modules/customerio-reactnative'
end

target 'NotificationServiceExtension' do
  pod 'customerio-reactnative-richpush/fcm', :path => '../node_modules/customerio-reactnative'
end
```

- Install `@react-native-firebase/messaging` as well as `@react-native-firebase/app`. Without FirebaseMessaging in the app, `MessagingPushFCM.initialize` stops the app at launch.
- List `fcm-core` instead of `fcm`. Listing both brings Firebase back from CocoaPods.
- The Expo plugin always uses `fcm` for now.

---

## 🔴 Live Activities

Enable the activity types you use under the `liveNotifications` key of your SDK config. On iOS, also add `NSSupportsLiveActivities` to your app's `Info.plist`, the `liveactivities` pod subspec, and a Widget Extension that renders the SDK's built-in templates. Without the `Info.plist` key, iOS refuses to start any activity:

```xml
<key>NSSupportsLiveActivities</key>
<true/>
```

**One manual step is required on iOS.** Forward every opened URL to the SDK from the host's active lifecycle, or taps on a Live Activity are not attributed. `NativeLiveActivities` comes from the wrapper pod, so import it. This only compiles once the `liveactivities` subspec is installed.

For an AppDelegate host:

```swift
import customerio_reactnative
import React

extension AppDelegate {
  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    // Reports an `opened` metric and returns the deep link to route to. A non-Customer.io URL comes
    // back unchanged; `nil` means the activity carried no deep link, so there is nothing to open.
    guard let routableUrl = NativeLiveActivities.handleWidgetUrl(url) else { return true }
    return RCTLinkingManager.application(app, open: routableUrl, options: options)
  }
}
```

A React Native `AppDelegate` conforms to `UIApplicationDelegate` directly rather than subclassing, so this method is not an `override`, and the URL is passed on to `RCTLinkingManager` instead of `super`. See [the sample app's `AppDelegate.swift`](/example/ios/SampleApp/AppDelegate.swift) for this in context.

For a `UIScene` host, handle both lifecycle paths. At scene connection, pass the connection options through the wrapper so a cold Live Activity tap is attributed and its destination enters the acknowledged Customer.io router. Ordinary app links remain in React Native launch options for `Linking`:

```swift
import customerio_reactnative

reactNativeFactory?.startReactNative(
  withModuleName: "YourApp",
  in: window,
  launchOptions: NativeLiveActivities.reactNativeLaunchOptions(from: connectionOptions)
)
```

Then replace the ordinary React Native URL-forwarding body in the existing `SceneDelegate` for warm opens. This reports Live Activity taps, sends Customer.io destinations through the configured Customer.io router, and forwards ordinary app links to React Native `Linking`:

```swift
import customerio_reactnative

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  for context in URLContexts {
    NativeLiveActivities.handleAndRouteWidgetUrl(context.url)
  }
}
```

Do not also pass those URLs to `RCTLinkingManager`. The helper already routes them through the
Customer.io bridge, and forwarding them again can deliver the same URL twice.

Android needs no equivalent native step.

For Expo Router, unwrap the URL once in the app's top-level `app/+native-intent.tsx` file:

```typescript
import { CustomerIO } from 'customerio-reactnative';

export async function redirectSystemPath({ path }: { path: string }) {
  return CustomerIO.liveActivities.handleWidgetUrl(path);
}
```

This reports the opened event, returns the customer's destination, preserves ordinary URLs, and
returns `null` for a Customer.io tracking URL without a destination. Do not also call the helper
from a `Linking` listener because processing the same tracking URL twice reports two opened events.
Expo apps without Expo Router can apply the helper once in their central `Linking` initial-URL and
subscription pipeline.

---

## Identify Users, Track Events, and More

Customer.io helps you personalize your mobile experience:

- 👤 [Identify users](https://customer.io/docs/sdk/react-native/identify/) to associate events and devices with profiles
- 📊 [Track events](https://customer.io/docs/sdk/react-native/track-events/) to trigger messaging based on user behavior
- 💬 [In-App Messages](https://customer.io/docs/sdk/react-native/in-app/) are server-driven and blend seamlessly with your app

---

## Contributing

We welcome contributions! To get started:

1. Review our [example app](/example) to help with local development.
2. Follow our [Code of Conduct](CODE_OF_CONDUCT.md).

---

## License

[MIT](LICENSE)

---
_Source: https://npm.io/package/customerio-reactnative · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
