Customerly is a customer service platform that helps businesses provide better support to their customers. The React Native SDK allows you to integrate Customerly's features directly into your React Native application, including:
- Live chat support
- Help center articles
- User profiling
- Event tracking
- Lead generation
- Surveys
- Real-time video calls
Add the SDK to your project:
yarn add react-native-customerly-sdkThis library needs these dependencies to be installed in your project before you can use it:
yarn add react-native-webview react-native-safe-area-context react-native-device-infoUsing Expo?
npx expo install react-native-webview react-native-safe-area-context react-native-device-infoWant the SDK to show a notification when a message arrives? That needs one extra package — see Notifications. It is entirely optional; everything else works without it.
Finally, if you use Expo, rebuild the native code:
npx expo prebuild --cleanThis helps avoid build issues, especially after adding or updating native dependencies.
Wrap your app with CustomerlyProvider (it must be wrapped in a SafeAreaProvider) and use the Customerly API:
import React from "react";
import { CustomerlyProvider, Customerly } from "react-native-customerly-sdk";
import { SafeAreaProvider } from "react-native-safe-area-context";
export default function App() {
return (
<SafeAreaProvider>
<CustomerlyProvider appId="YOUR_APP_ID">{/* Your app content */}</CustomerlyProvider>
</SafeAreaProvider>
);
}You can then use the Customerly API anywhere in your app:
import { Customerly } from "react-native-customerly-sdk";
Customerly.show();
Customerly.update({
appId: "YOUR_APP_ID",
userId: "123",
email: "user@example.com",
name: "John Doe",
});The SDK can post a local notification whenever a message arrives while the messenger is closed, and open the right conversation when the user taps it.
The SDK does not depend on a notification library. It has no notification package in its dependencies and imports none at runtime. Instead, you install the one that fits your app and hand it to CustomerlyProvider. This is deliberate: Notifee — the package the SDK used to depend on — was archived in April 2026, and its maintained successor does not support the legacy React Native architecture. Rather than force either choice on you, the SDK works with both, and with neither.
| Your app | Install |
|---|---|
New Architecture (newArchEnabled=true) |
yarn add react-native-notify-kit |
Legacy Architecture (newArchEnabled=false) |
yarn add @notifee/react-native |
| Already handles push notifications itself | nothing — skip this whole section |
Not sure which architecture you are on? React Native enables the New Architecture by default from 0.76, and 0.82 removed the legacy one entirely. If you have never set newArchEnabled and you are on 0.76+, you are on the New Architecture.
react-native-notify-kit is the maintained fork of Notifee, recommended by Invertase when they archived the original. It is New Architecture only — it is built as a TurboModule with no legacy bridge support, so it will not work with newArchEnabled=false.
@notifee/react-native is archived and no longer receives fixes, but it still works and remains the only option on the legacy architecture. Move to react-native-notify-kit when you migrate to the New Architecture.
Both expose an identical API, so the SDK drives either one without any change on your side.
Expo — add the config plugin to your app.json and rebuild:
{
"expo": {
"plugins": ["react-native-notify-kit"]
}
}npx expo prebuild --cleanNotifications need native code, so they do not work in Expo Go. Use a development build.
Bare React Native — autolinking handles Android. On iOS, install the pods:
cd ios && pod installIf you are on @notifee/react-native with Expo, you also need its Maven workaround, which react-native-notify-kit no longer requires:
{
"expo": {
"plugins": [
[
"expo-build-properties",
{ "android": { "extraMavenRepos": ["../../node_modules/@notifee/react-native/android/libs"] } }
]
]
}
}Import the package's default export and pass it as notificationsModule:
import React from "react";
import { Customerly, CustomerlyProvider } from "react-native-customerly-sdk";
import notifee from "react-native-notify-kit"; // or "@notifee/react-native" on the legacy architecture
import { SafeAreaProvider } from "react-native-safe-area-context";
export default function App() {
return (
<SafeAreaProvider>
<CustomerlyProvider appId="YOUR_APP_ID" notificationsModule={notifee}>
{/* Your app content */}
</CustomerlyProvider>
</SafeAreaProvider>
);
}That is the only wiring required. Tapping a notification opens the conversation it belongs to.
Pass a stable reference here — a module import, as above, or a module-level constant. Do not build the object inline in JSX: the SDK keys its notification effects on this value, so a fresh identity on every render re-subscribes the foreground listener and re-reads the initial notification.
If you omit notificationsModule, the SDK never posts a notification and every notification-related method resolves without doing anything. Nothing else changes and nothing throws — so leave it out if your app already handles push on its own. The first time a message arrives with no module wired up, the SDK logs a one-off console.warn in development builds (never in release) so an accidentally missing prop does not go unnoticed.
Request the OS permission at a moment that makes sense in your app:
Customerly.requestNotificationPermissionIfNeeded();Like every other Customerly method, this requires a mounted CustomerlyProvider, so call it from inside the provider's subtree — a useEffect in a component rendered as a child of CustomerlyProvider is the usual place. Called with no provider mounted it returns a rejected promise, so you can .catch() it. It resolves without doing anything if you did not pass a notificationsModule.
Pass getNotificationSetup in your settings to decide, per message, whether to show a notification and what it should say. Return { shouldShow: false } to suppress it:
Customerly.update({
appId: "YOUR_APP_ID",
getNotificationSetup: (message) => {
if (userIsInsideOwnChatScreen) {
return { shouldShow: false };
}
return {
shouldShow: true,
title: message.accountName,
body: message.message,
// Android only, overrides the provider-level channel for this message
notificationChannelId: "customerly-urgent",
notificationChannelName: "Urgent support replies",
};
},
});When title is omitted the SDK uses the message text with its HTML stripped. The Android channel used for every other notification is set with the notificationChannelId / notificationChannelName props on CustomerlyProvider.
notificationsModule is typed as NotificationsModule, a five-method structural contract rather than a reference to any specific package. Anything matching it works, so you can adapt whatever your app already uses:
import type { NotificationsModule } from "react-native-customerly-sdk";
const myNotifications: NotificationsModule = {
requestPermission: async () => ({ authorizationStatus: 1 }), // 1 = authorized
createChannel: async ({ id }) => id,
displayNotification: async (notification) => {
/* show it with your library */ return "id";
},
onForegroundEvent: (observer) => {
/* call observer({ type: 1, detail: { notification } }) on tap */ return () => {};
},
getInitialNotification: async () => null,
};The numeric values mirror the notifee enums the contract was modelled on: authorizationStatus: 1 means authorized, and event type: 1 (press) or 2 (action press) is what routes a tap back into the messenger. The data you attach in displayNotification must be handed back unchanged on tap — that is how the SDK knows which conversation to open.
v2 removes the hard dependency on @notifee/react-native. If you were using notifications, there are two changes to make:
1. Pass your notification module to the provider. Previously the SDK imported Notifee itself; now you pass it in:
+import notifee from "react-native-notify-kit";
+
-<CustomerlyProvider appId="YOUR_APP_ID">
+<CustomerlyProvider appId="YOUR_APP_ID" notificationsModule={notifee}>To keep using Notifee, change nothing but the provider prop — import notifee from "@notifee/react-native" and pass that instead. To move to the maintained fork, follow Step 1; it is a package swap and an import rename, since the two APIs are identical.
If you do not pass the prop, the SDK stops posting notifications — it will not throw, so this is easy to miss. Development builds log a one-off console.warn the first time a message arrives without a module, which is your cue that the prop is missing. Nothing else in the API changed.
2. requestNotificationPermissionIfNeeded now requires a mounted provider. It used to call Notifee directly and worked anywhere; it now goes through the provider like every other method and rejects with "CustomerlyProvider is not mounted." if called outside it. Move the call inside the provider's subtree if it was above it.
Also in this release: the async methods (requestNotificationPermissionIfNeeded, getUnreadMessagesCount, getUnreadConversationsCount) now reject when no provider is mounted instead of throwing synchronously. If you were wrapping the two count methods in try/catch to handle that, .catch() now works as the Promise<number> signature suggests — and a bare try/catch around the call alone no longer catches it.
You can also now drop @notifee/react-native entirely if your app handles push itself — the SDK no longer lists any notification package as a peer dependency.
| Prop | Type | Required | Description | Default |
|---|---|---|---|---|
appId |
string |
Yes | The Customerly app ID | |
colorScheme |
"light" | "dark" |
No | Your app's current color scheme | System color scheme |
notificationsModule |
NotificationsModule |
No | Default export of react-native-notify-kit or @notifee/react-native. Omit to disable notifications entirely. See Notifications |
- |
notificationChannelId |
string |
No | The ID of the notification channel to use for notifications (Android only, requires notificationsModule) |
customerly-notification-channel |
notificationChannelName |
string |
No | The name of the notification channel to use for notifications (Android only, requires notificationsModule) |
Customerly Notification Channel |
userId |
string |
No | The user ID | - |
name |
string |
No | The user name | - |
email |
string |
No | The user email | - |
accentColor |
string |
No | The accent color | The messenger accent color configured in your project settings |
contrastColor |
string |
No | The contrast color | The messenger contrast color configured in your project settings |
attachmentsAvailable |
boolean |
No | Whether attachments are available | true |
Updates the Customerly SDK settings in place, without restarting the messenger session (the current conversation and state are preserved).
Customerly.update({ appId: "YOUR_APP_ID" });Note:
updateno longer reloads the messenger. If you need a fresh session (the previous behavior), callreset— on its own, or afterupdateto apply new settings and then restart.
Restarts the messenger by reloading it from scratch, creating a fresh session. The messenger is re-initialized with the current settings, so the user stays logged in. Use this if you need the messenger to fully re-initialize (this is what update used to do implicitly).
Customerly.reset();
// Apply new settings and restart the session:
Customerly.update({ appId: "YOUR_APP_ID", userId: "123" });
Customerly.reset();Requests notification permissions if not already granted, through the notificationsModule passed to CustomerlyProvider. No-op when no module was passed. See Notifications for the full setup.
Customerly.requestNotificationPermissionIfNeeded();Shows the Customerly chat interface.
Customerly.show(withoutNavigation?: boolean);Hides the Customerly chat interface.
Customerly.hide();Navigates back in the chat interface.
Customerly.back();Logs out the current user.
Customerly.logout();Registers a new lead with the provided email and optional attributes.
Customerly.registerLead("user@example.com", { name: "John Doe" });Shows the chat interface with a pre-filled message.
Customerly.showNewMessage("Hello, how can I help you?");Sends a new message and shows the chat interface.
Customerly.sendNewMessage("Hello, how can I help you?");Opens the "book a meeting" calendar flow in the messenger.
Customerly.showBookMeeting();Navigates to a specific conversation.
Customerly.navigateToConversation(123);Shows an article from the help center, either by collection + article slug or by numeric article id.
Customerly.showArticle("collection", "article");
Customerly.showArticle(123); // by article idTracks a custom event.
Customerly.event("event_name");Sets a custom attribute for the current user.
Customerly.attribute("attribute_name", "attribute_value");Gets the count of unread messages.
await Customerly.getUnreadMessagesCount();Gets the count of unread conversations.
await Customerly.getUnreadConversationsCount();The SDK provides various callbacks for different events. Here are the main callback setters:
Customerly.setOnChatClosed(() => {});
Customerly.setOnChatOpened(() => {});
Customerly.setOnHelpCenterArticleOpened((article) => {});
Customerly.setOnLeadGenerated((email) => {});
Customerly.setOnMessageRead((conversationId, conversationMessageId) => {});
Customerly.setOnMessengerInitialized(() => {});
Customerly.setOnNewConversation((message, attachments) => {});
Customerly.setOnNewMessageReceived((message) => {});
Customerly.setOnNewConversationReceived((conversationId) => {});
Customerly.setOnProfilingQuestionAnswered((attribute, value) => {});
Customerly.setOnProfilingQuestionAsked((attribute) => {});
Customerly.setOnRealtimeVideoAnswered((realtimeCall) => {});
Customerly.setOnRealtimeVideoCanceled(() => {});
Customerly.setOnRealtimeVideoReceived((realtimeCall) => {});
Customerly.setOnRealtimeVideoRejected(() => {});
Customerly.setOnSurveyAnswered(() => {});
Customerly.setOnSurveyPresented((survey) => {});
Customerly.setOnSurveyRejected(() => {});All public types (CustomerlySettings, Message, Survey, RealtimeCall, HelpCenterArticle, AttachmentPayload, NotificationSetup, NotificationsModule, callback payload types, etc.) are exported from the package root for typing your handlers:
import type { CustomerlySettings, Survey } from "react-native-customerly-sdk";Each callback has a corresponding remove method:
Customerly.removeOnChatClosed();
Customerly.removeOnChatOpened();
// ... and so on for all callbacksYou can also remove all callbacks at once:
Customerly.removeAllCallbacks();The repository includes a sample project (example) that demonstrates how to integrate and use the Customerly SDK in a Expo application. The example shows:
- Basic SDK initialization
- Messenger presentation
- User management
- Event tracking
- Message handling
- Notification handling (wired up with
react-native-notify-kit) - Callback usage
To run the example:
- Run
yarn installto install the dependencies - Run
yarn example:iosto start the iOS simulator - Run
yarn example:androidto start the Android emulator
The sample app provides a complete reference implementation of all SDK features and can be used as a starting point for your integration.
To release a new version of the SDK, you need to:
- Go to GitHub Actions and run the
Releaseworkflow - The workflow will build the SDK and release it to npm
Copyright 2025-2026 Customerly Ltd.
This SDK is licensed under the Apache License 2.0 — you are free to use it in commercial and closed-source applications. See the LICENSE and NOTICE files for details.
Versions up to and including
2.0.1were published under the GNU GPLv3. The Apache 2.0 license applies from the next release onwards. If you are pinned to an older version, upgrade to pick up the more permissive terms.