> This page is part of the [Customer.io documentation](https://docs.customer.io). For the complete index, see [llms.txt](https://docs.customer.io/llms.txt).
> Last updated: August 19, 2026

# Set up live notifications

## How it works

Live notifications show information that changes over time in places your customers already look: the iOS Lock Screen and the Android notification panel. On iOS they’re built on Apple’s Live Activities; on Android, they’re built on Google’s Live Updates.

The React Native SDK includes two built-in templates that both native SDKs render for you—a multi-step tracker and a countdown timer—so you don’t need to write a platform-specific UI for either one. You can also define a single custom type and render it yourself.

Four actors drive every live notification:

*   **You** enable the notification types your app uses, and start notifications from JavaScript when something happens in your app.
*   **The SDK** registers the push tokens each notification needs. It also reports to Customer.io when your app starts or ends a notification, and when a customer dismisses one. Updates your app makes on the device aren’t reported.
*   **Your server** starts, updates, and ends notifications by calling [the API](/integrations/api/app/#tag/live-notifications). It never talks to APNs or FCM directly.
*   **Customer.io** turns each API call into a push, using the tokens the SDK registered, and records every start, update, and end as a delivery.

For the concepts behind the feature and the operations your server calls, see the [live notifications overview](/messaging/channels/live-notifications/overview/).

## Prerequisites

Live notifications use the push credentials your app already has in Customer.io. Before you begin:

*   Set up [push notifications](/integrations/sdk/react-native/push-notifications/push/) for your app.
*   You must identify your app user before you can start a notification. The SDK holds token registrations until you identify a person, and [the API](/integrations/api/app/#tag/live-notifications/startLiveNotification) rejects requests that target anonymous profiles.
*   On iOS, target **iOS 16.2 or later**. Starting a notification from your server (push-to-start) requires iOS 17.2.
*   On Android, request `POST_NOTIFICATIONS` on Android 13 and later. To get Android 16’s promoted **Live Updates** treatment, also declare `POST_PROMOTED_NOTIFICATIONS` in your manifest. Without it, live notifications still render as ongoing notifications.

## Set up your Android project

You don’t need to do any build-time setup on Android. Live notifications are part of the push module, so they’re available as soon as you [configure them in your initialization config](#initialize-the-sdk-with-live-notifications).

## Set up your iOS project

To support iOS, you need to enable the Live Activities module, add a widget extension to render your notifications, and forward widget URLs to the SDK so taps are attributed.

### Enable the Live Activities module

Add the `liveactivities` subspec to your `ios/Podfile`. CocoaPods takes one line per pod, so **replace** the `customerio-reactnative` line you added during [push setup](/integrations/sdk/react-native/push-notifications/push/) with a single line listing every subspec you use—your push provider (`apn` or `fcm`) included:

```
pod "customerio-reactnative", :path => "../node_modules/customerio-reactnative",
    :subspecs => ["apn", "liveactivities"]
```

The `:path` is required. Without it CocoaPods looks for the pod in its trunk repo instead of the copy in your `node_modules`, and `pod install` fails.

Your app target needs use\_frameworks!

A Live Activity widget links Swift pods, and CocoaPods requires an app target and the extension it embeds to agree on `use_frameworks!`. If your app target doesn’t have it, the build fails with `could not build module 'customerio_reactnative'`. Set it once, above your targets, and use static linkage—React Native’s New Architecture doesn’t support dynamic frameworks:

```
use_frameworks! :linkage => :static
```

This applies on both push providers, not just Firebase.

Then set `NSSupportsLiveActivities` in your app target’s `Info.plist`. Without it, iOS refuses to start any notification, so nothing appears even when everything else is configured:

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

### Add a widget extension

iOS renders live notifications from a widget extension, so your app needs one even when you only use the built-in templates. In Xcode, choose **File > New > Target > Widget Extension**.

A widget extension can’t link the wrapper pod, so name the rendering pods in its own Podfile target. Keep the version in step with the native iOS SDK version the wrapper pins, which you’ll find as `cioNativeiOSSdkVersion` in the SDK’s `package.json`:

```
target "LiveActivityWidget" do
  pod "CustomerIOLiveActivitiesTemplates", "4.7.2"
  pod "CustomerIOLiveActivitiesAttributes", "4.7.2"
end
```

That pin is the native version the wrapper ships with today. Read yours before you copy it—an extension built against a different native SDK than your app can fail to link.

Then add the SDK’s built-in widgets to your extension’s `WidgetBundle`. Both ship in `CioLiveActivities_Templates`:

```
import CioLiveActivities_Templates
import SwiftUI
import WidgetKit

@main
struct LiveActivityWidgetBundle: WidgetBundle {
  var body: some Widget {
    CIOSegmentsLiveActivity()
    CIOCountdownTimerLiveActivity()
  }
}
```

List only the widgets for the templates you enabled.

### Forward widget URLs to the SDK

When someone taps a live notification, iOS opens your app with the widget URL. Forward it to the SDK from your `AppDelegate` to attribute the tap to the notification:

```
import customerio_reactnative
import React

extension AppDelegate {
  func application(_ app: UIApplication, open url: URL,
                   options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    guard let routableUrl = NativeLiveActivities.handleWidgetUrl(url) else { return true }
    return RCTLinkingManager.application(app, open: routableUrl, options: options)
  }
}
```

`handleWidgetUrl` reports the `opened` metric and returns the URL to route to. A URL that didn’t come from Customer.io comes back unchanged. `nil` means the notification did not contain a deep link, so there’s nothing to open.

A React Native `AppDelegate` conforms to `UIApplicationDelegate` directly rather than inheriting from a parent class, so this method isn’t an `override`, and you pass the URL to `RCTLinkingManager` rather than `super`. Android needs no equivalent step.

## Initialize the SDK with live notifications

Add a `liveNotifications` object to your initialization config, listing the built-in types your app uses:

```
import { CustomerIO, CioRegion, LiveActivityTemplate } from 'customerio-reactnative';

CustomerIO.initialize({
  cdpApiKey: 'your-cdp-api-key',
  region: CioRegion.US,
  liveNotifications: {
    types: [
      LiveActivityTemplate.Segments,
      LiveActivityTemplate.CountdownTimer,
    ],
  },
});
```

Enabling a type registers it for push-to-start, so your server can create that notification remotely. Unrecognized identifiers are ignored, so a template added in a newer native SDK won’t break an older build of your app.

### Branding on Android

Android renders Customer.io’s built-in templates itself, so you can pass a `branding` object to customize their appearance. iOS ignores it, because your widget extension’s SwiftUI defines the appearance there.

```
liveNotifications: {
  types: [LiveActivityTemplate.Segments],
  branding: {
    accentColorHex: '#1B5E20',
    logoResource: 'brand_logo',
    smallIconResource: 'ic_notification',
  },
},
```

`logoResource` and `smallIconResource` name bundled Android drawable resources. You can use `logoUrl` for a remote logo instead, but a bundled resource renders on the first frame without a network round-trip.

## Render a custom type

To send data that Customer.io’s built-in templates don’t support by default, you can name your own type with `customType` and render it on both platforms yourself. Don’t add `Custom` to `types`—`customType` is what enables it:

```
liveNotifications: {
  types: [LiveActivityTemplate.Segments],
  customType: 'com.myapp.rideshare',
},
```

Then start it with the `Custom` template:

```
await CustomerIO.liveActivities.start({
  type: LiveActivityTemplate.Custom,
  data: { driver: 'Sam', vehicle: 'Blue Prius', minutesAway: '4' },
});
```

On each platform, you need to render the notification:

*   **iOS**: render the SDK’s `CIOCustomAttributes` in your widget extension with your own SwiftUI.
*   **Android**: implement `createLiveNotification` in a `CustomerIOLiveNotificationsCallback` and register it with `NativeLiveActivitiesModule.setLiveNotificationCallback`. Register it in `Application.onCreate`, before the SDK initializes—the native SDK only accepts the callback at build time. Your callback receives the same `data` map as the one you pass to `start`.

[GitHub project](https://github.com/customerio/customerio-reactnative "View the React Native SDK on GitHub")

Version

6.6.2 (Current)5.x (5.3.0)4.x (4.11.0)3.x (3.9.1)2.x (2.5.1)

©2026 Peaberry Software, Inc. [Status](https://status.customerio.com/) [Terms of Service](https://customer.io/legal/terms-of-service/) [Privacy Policy](https://customer.io/legal/privacy-policy/)

[](https://www.linkedin.com/company/customer-io)[](https://twitter.com/customerio)[](https://www.youtube.com/channel/UCkCaWdezRoa8ZyR9pEVaipA)[](https://www.instagram.com/customer.io/)
