The official Crisp SDK for React Native — Add live chat to your Expo & React Native apps
See Crisp Support Chat SDKs • React Native Support Chat SDK • Developer Docs
Trusted by
Fruitz
·
and many more companies rely on Crisp to power their in-app customer support.
Warning
Expo SDK 53+ Required
This SDK is exclusively compatible with Expo SDK version 53 and newer. For projects using older Expo versions, please use the legacy React Native SDK. If you're migrating from the legacy SDK, see the Migration Guide.
Warning
Expo Go is Not Supported
The Crisp SDK uses native modules that are not available in Expo Go. You must use a development build:
npx expo run:ios
# or
npx expo run:androidInstall the SDK using your preferred package manager:
# Using bun
bunx expo install crisp-sdk-react-native
# Using pnpm
pnpm dlx expo install crisp-sdk-react-native
# Using npm
npx expo install crisp-sdk-react-native
# Using yarn
yarn dlx expo install crisp-sdk-react-nativeThe Crisp SDK requires minimum OS versions to function properly. Install expo-build-properties and configure your app.json:
npx expo install expo-build-properties{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"ios": {
"deploymentTarget": "15.1"
},
"android": {
"minSdkVersion": 21
}
}
]
]
}
}Note
This ensures your project targets iOS 15.1+ and Android SDK 21+, which are required by the native Crisp SDKs.
This guide is for React Native developers who want to integrate Crisp using the Expo SDK in a project that doesn't use Expo as its development framework.
Before starting, ensure you have:
- React Native 0.79+
- iOS deployment target 15.1+
- Android minSdkVersion 21+
- Node.js 18+
Run the following command in your project root:
npx install-expo-modules@latestThis command automatically configures your iOS and Android projects to support Expo modules.
Note
For comprehensive installation details or manual installation steps, refer to Expo's official guide.
# Using npm
npm install crisp-sdk-react-native
# Using yarn
yarn add crisp-sdk-react-native
# Using pnpm
pnpm add crisp-sdk-react-native
# Using bun
bun add crisp-sdk-react-nativeiOS:
cd ios && pod installAndroid:
Ensure your android/app/build.gradle has:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
}
}To use the Crisp SDK, you need your Website ID from the Crisp Dashboard.
- Sign up for a free account on Crisp (or log in)
- Go to Settings > Website Settings > Setup instructions
- Copy your Website ID
Configure the SDK at app startup with your Website ID:
import { useEffect } from "react";
import { configure } from "crisp-sdk-react-native";
export default function App() {
useEffect(() => {
configure("YOUR_WEBSITE_ID");
}, []);
return (
// Your app content
);
}To enable push notifications, add the config plugin to your app.json or app.config.js:
{
"expo": {
"plugins": [
[
"crisp-sdk-react-native",
{
"websiteId": "YOUR_WEBSITE_ID",
"notifications": {
"enabled": true,
"mode": "sdk-managed"
}
}
]
]
}
}| Option | Type | Default | Description |
|---|---|---|---|
websiteId |
string |
- | Your Crisp Website ID. Required when notifications are enabled. |
notifications.enabled |
boolean |
false |
Enable push notifications for Crisp Chat. |
notifications.mode |
"sdk-managed" | "coexistence" |
"sdk-managed" |
Notification handling mode. See Coexistence Mode below. |
Important
The websiteId is required when notifications.enabled is true. The plugin will throw an error if it's missing.
iOS:
- Adds
remote-notificationto UIBackgroundModes - Adds
aps-environmententitlement for APNs - Registers for remote notifications on app launch
- Forwards device token to Crisp SDK
Android:
- Adds
CrispNotificationServiceto AndroidManifest - Adds
firebase-messagingdependency - Configures Crisp SDK with websiteId
Note
After enabling notifications, rebuild your app with npx expo prebuild --clean followed by npx expo run:ios or npx expo run:android.
Push notifications require additional setup in your Crisp Dashboard (APNs for iOS, Firebase for Android).
See the Push Notifications Setup Guide for detailed step-by-step instructions.
By default, Crisp handles all push notification routing exclusively ("sdk-managed" mode). If your app uses another notification system (like expo-notifications, @react-native-firebase/messaging, or OneSignal), use "coexistence" mode to let both systems work together:
{
"expo": {
"plugins": [
[
"crisp-sdk-react-native",
{
"websiteId": "YOUR_WEBSITE_ID",
"notifications": {
"enabled": true,
"mode": "coexistence"
}
}
]
]
}
}In coexistence mode, the plugin:
- Android: Generates a chained
FirebaseMessagingServicethat routes Crisp notifications to the Crisp SDK and delegates all others toexpo-notifications(or Firebase directly) - iOS: Implements a
UNUserNotificationCenterDelegatethat filters Crisp notifications and forwards the rest to the previous delegate (chain of responsibility)
JS API for coexistence mode:
import {
registerPushToken,
isCrispPushNotification,
setShouldPromptForNotificationPermission,
} from "crisp-sdk-react-native";
// Register a push token obtained from your notification system
registerPushToken(expoPushToken);
// Check if a notification payload is from Crisp
const isCrisp = isCrispPushNotification(notificationData);
// Control whether Crisp auto-prompts for notification permissions (iOS only)
setShouldPromptForNotificationPermission(false);Listen for Crisp notifications in the foreground (iOS only):
import { useCrispEvents } from "crisp-sdk-react-native";
useCrispEvents({
onPushNotificationReceived: ({ title, body }) => {
console.log("Crisp notification:", title, body);
// Update badge count, show toast, log analytics, etc.
},
});Note
onPushNotificationReceived is currently iOS only. On Android, the Crisp SDK does not expose a foreground notification callback — notifications are handled entirely at the native FirebaseMessagingService level.
Note
In coexistence mode, the native routing is automatic — you don't need to write JS filtering code. The JS API methods (registerPushToken, isCrispPushNotification) are optional utilities for advanced use cases.
Display the Crisp chat widget:
import { show } from "crisp-sdk-react-native";
function ChatButton() {
const openChat = () => {
show();
};
return <Button title="Chat with us" onPress={openChat} />;
}Set user information to personalize the chat experience:
import {
setUserEmail,
setUserNickname,
setUserPhone,
setUserAvatar,
setUserCompany,
setTokenId,
resetSession,
} from "crisp-sdk-react-native";
// After user logs in
function identifyUser(user) {
// Basic identification
setUserEmail(user.email);
setUserNickname(user.name);
setUserPhone(user.phone); // E.164 format recommended: "+1234567890"
setUserAvatar(user.avatarUrl);
// Set company information
setUserCompany({
name: "Acme Corporation",
url: "https://acme.com",
companyDescription: "Leading provider of innovative solutions",
employment: {
title: "Software Engineer",
role: "Engineering",
},
geolocation: {
country: "France",
city: "Paris",
},
});
// Enable session persistence across devices
setTokenId(user.id);
}
// On logout
function onLogout() {
setTokenId(null);
resetSession();
}Store custom data visible to operators in the Crisp dashboard:
import {
setSessionString,
setSessionBool,
setSessionInt,
setSessionSegment,
setSessionSegments,
getSessionIdentifier,
} from "crisp-sdk-react-native";
// Store different data types
setSessionString("plan", "premium");
setSessionBool("verified", true);
setSessionInt("loginCount", 42);
// Categorize users with segments
setSessionSegment("vip");
// Or set multiple segments at once
setSessionSegments(["premium", "early-adopter", "beta-tester"]);
// Replace all existing segments
setSessionSegments(["enterprise"], true);
// Get the current session identifier
const sessionId = await getSessionIdentifier();
console.log("Session ID:", sessionId);Track user actions in the chat timeline:
import {
pushSessionEvent,
pushSessionEvents,
CrispSessionEventColors,
} from "crisp-sdk-react-native";
// Track a single event
pushSessionEvent("Purchase completed", CrispSessionEventColors.GREEN);
pushSessionEvent("Payment failed", CrispSessionEventColors.RED);
// Track multiple events at once
pushSessionEvents([
{ name: "Viewed pricing page", color: CrispSessionEventColors.BLUE },
{ name: "Started free trial", color: CrispSessionEventColors.GREEN },
{ name: "Upgraded to Pro", color: CrispSessionEventColors.PURPLE },
]);Subscribe to SDK events using the useCrispEvents hook:
import { useState } from "react";
import { View, Button } from "react-native";
import { show, useCrispEvents } from "crisp-sdk-react-native";
function ChatScreen() {
const [unreadCount, setUnreadCount] = useState(0);
useCrispEvents({
onSessionLoaded: (sessionId) => {
console.log("Crisp session ready:", sessionId);
},
onChatOpened: () => {
console.log("Chat opened");
setUnreadCount(0); // Reset unread count
},
onChatClosed: () => {
console.log("Chat closed");
},
onMessageSent: (message) => {
console.log("User sent:", message.content);
},
onMessageReceived: (message) => {
console.log("Received:", message.content);
if (message.fromOperator) {
setUnreadCount((count) => count + 1);
}
},
});
return (
<View>
<Button title={`Open Chat (${unreadCount})`} onPress={() => show()} />
</View>
);
}Display messages programmatically in the chat:
import { showMessage } from "crisp-sdk-react-native";
// Simple text message
showMessage({
type: "text",
text: "Hello! How can I help you today?",
});
// File attachment
showMessage({
type: "file",
url: "https://example.com/document.pdf",
name: "Document.pdf",
mimeType: "application/pdf",
});
// Animation (GIF)
showMessage({
type: "animation",
url: "https://example.com/celebration.gif",
mimeType: "image/gif",
});
// Audio message
showMessage({
type: "audio",
url: "https://example.com/voice-note.mp3",
mimeType: "audio/mpeg",
duration: 15,
});
// Picker for user choice
showMessage({
type: "picker",
id: "satisfaction",
text: "How satisfied are you with our service?",
choices: [
{ value: "happy", label: "Very satisfied" },
{ value: "neutral", label: "Neutral" },
{ value: "sad", label: "Not satisfied" },
],
});
// Field for user input
showMessage({
type: "field",
id: "email",
text: "What's your email address?",
explain: "We'll send you updates",
required: true,
});
// Carousel with multiple items
showMessage({
type: "carousel",
text: "Check out our products",
targets: [
{
title: "Product A",
description: "Great for beginners",
imageUrl: "https://example.com/product-a.jpg",
actionUrl: "https://example.com/products/a",
},
{
title: "Product B",
description: "For power users",
imageUrl: "https://example.com/product-b.jpg",
actionUrl: "https://example.com/products/b",
},
],
});Access your knowledge base:
import { searchHelpdesk, openHelpdeskArticle } from "crisp-sdk-react-native";
// Open helpdesk search (automatically opens the chat)
searchHelpdesk();
// Open a specific article (automatically opens the chat)
openHelpdeskArticle({
id: "getting-started",
locale: "en",
title: "Getting Started", // Optional
category: "Onboarding", // Optional
});Trigger automated conversation flows:
import { runBotScenario } from "crisp-sdk-react-native";
// Start a bot scenario configured in your Crisp dashboard
runBotScenario("welcome-flow");Enable native SDK logging to help debug integration issues:
import {
setLogLevel,
CrispLogLevel,
useCrispEvents,
} from "crisp-sdk-react-native";
// Set the minimum log level (default: WARN)
setLogLevel(CrispLogLevel.DEBUG);
// Listen to log messages from the native SDK
useCrispEvents({
onLogReceived: (log) => {
console.log(`[Crisp] [${log.level}] ${log.tag}: ${log.message}`);
},
});Available log levels (from most to least verbose):
| Level | Value | Description |
|---|---|---|
VERBOSE |
0 | Most verbose, all log messages |
DEBUG |
1 | Debug information |
INFO |
2 | Informational messages |
WARN |
3 | Warnings (default) |
ERROR |
4 | Error messages only |
ASSERT |
5 | Critical assertion failures |
Note
Set the log level after calling configure(). Only logs at or above the configured level are emitted to the onLogReceived callback.
| Method | Description | Parameters | Return |
|---|---|---|---|
configure(websiteId) |
Initialize the SDK with your Website ID. Must be called once at app startup. | websiteId: string |
void |
setTokenId(tokenId) |
Set a token for session persistence across app reinstalls and devices. | tokenId: string | null |
void |
| Method | Description | Parameters | Return |
|---|---|---|---|
setLogLevel(level) |
Set the minimum log level for native SDK logging. Logs at or above this level are emitted via onLogReceived. |
level: CrispLogLevel |
void |
| Method | Description | Parameters | Return |
|---|---|---|---|
setUserEmail(email, signature?) |
Set the user's email address. Optional HMAC signature for verification. | email: string, signature?: string | null |
void |
setUserNickname(name) |
Set the user's display name in the chat. | name: string |
void |
setUserPhone(phone) |
Set the user's phone number. E.164 format recommended. | phone: string |
void |
setUserAvatar(url) |
Set the user's avatar image URL. | url: string |
void |
setUserCompany(company) |
Set the user's company information. | company: Company |
void |
| Method | Description | Parameters | Return |
|---|---|---|---|
setSessionString(key, value) |
Store a custom string value in session data. | key: string, value: string |
void |
setSessionBool(key, value) |
Store a custom boolean value in session data. | key: string, value: boolean |
void |
setSessionInt(key, value) |
Store a custom integer value in session data. | key: string, value: number |
void |
setSessionSegment(segment) |
Set a single segment to categorize the user. | segment: string |
void |
setSessionSegments(segments, overwrite?) |
Set multiple segments. If overwrite is true, replaces existing segments. |
segments: string[], overwrite?: boolean |
void |
getSessionIdentifier() |
Get the current session identifier. | - | Promise<string | null> |
| Method | Description | Parameters | Return |
|---|---|---|---|
pushSessionEvent(name, color) |
Track a single event in the user's chat timeline. | name: string, color: CrispSessionEventColors |
void |
pushSessionEvents(events) |
Track multiple events at once. | events: SessionEvent[] |
void |
| Method | Description | Parameters | Return |
|---|---|---|---|
resetSession() |
Clear the current session and start a fresh conversation. | - | void |
| Method | Description | Parameters | Return |
|---|---|---|---|
show() |
Open the Crisp chat widget. | - | void |
searchHelpdesk() |
Open the helpdesk search interface and the chat widget. | - | void |
openHelpdeskArticle(options) |
Open a specific helpdesk article and the chat widget. | options: HelpdeskArticleOptions |
void |
runBotScenario(scenarioId) |
Trigger an automated bot scenario. | scenarioId: string |
void |
| Method | Description | Parameters | Return |
|---|---|---|---|
registerPushToken(token) |
Register a push token (FCM/APNs) with Crisp. | token: string |
void |
isCrispPushNotification(data) |
Check if a notification payload is from Crisp. | data: Record<string, string> |
boolean |
setShouldPromptForNotificationPermission(enabled) |
Control auto-prompting for notification permissions (iOS only, no-op on Android). | enabled: boolean |
void |
| Method | Description | Parameters | Return |
|---|---|---|---|
showMessage(content) |
Display a message as operator in the local chatbox. | content: MessageContent |
void |
| Hook | Description | Parameters | Return |
|---|---|---|---|
useCrispEvents(callbacks) |
Subscribe to SDK events with automatic cleanup. | callbacks: CrispEventCallbacks |
void |
| Function | Description | Parameters | Return |
|---|---|---|---|
getSDKVersion() |
Get the crisp-sdk-react-native version string. | - | string |
interface Company {
name: string; // Required: Company name
url?: string; // Company website URL
companyDescription?: string; // Brief company description
employment?: Employment; // User's job details
geolocation?: Geolocation; // Company location
}interface Employment {
title?: string; // Job title (e.g., "Software Engineer")
role?: string; // Department/role (e.g., "Engineering")
}interface Geolocation {
country?: string; // Country name or ISO code
city?: string; // City name
}interface SessionEvent {
name: string; // Event name
color: CrispSessionEventColors; // Event color
}interface CrispMessage {
content: string; // Message text
timestamp: number; // Unix timestamp (ms)
fromOperator: boolean; // true if from operator
fingerprint: string; // Unique message ID
isMe: boolean; // true if sent by current user
origin: CrispMessageOrigin; // "local" | "network" | "update"
user?: CrispUser; // Sender information
}interface CrispUser {
nickname?: string; // Display name
userId?: string; // Unique identifier
avatar?: string; // Avatar URL
}The showMessage method accepts these content types:
{ type: "text", text: string }{ type: "file", url: string, name: string, mimeType: string }{ type: "animation", url: string, mimeType: string }{ type: "audio", url: string, mimeType: string, duration: number }{
type: "picker",
id: string,
text: string,
choices: Array<{ value: string, label: string, selected?: boolean }>
}{ type: "field", id: string, text: string, explain?: string, required?: boolean }{
type: "carousel",
text: string,
targets: Array<{
title: string,
description?: string,
imageUrl?: string,
actionUrl?: string
}>
}interface CrispEventCallbacks {
onSessionLoaded?: (sessionId: string) => void;
onChatOpened?: () => void;
onChatClosed?: () => void;
onMessageSent?: (message: CrispMessage) => void;
onMessageReceived?: (message: CrispMessage) => void;
onPushNotificationReceived?: (notification: PushNotificationPayload) => void; // iOS only
onLogReceived?: (log: CrispLogEntry) => void;
}These types are used internally by the event system:
// Payload for onSessionLoaded callback
interface SessionLoadedPayload {
sessionId: string;
}
// Payload for onMessageSent and onMessageReceived callbacks
interface MessagePayload {
message: CrispMessage;
}
// Empty payload for onChatOpened and onChatClosed callbacks
type EmptyPayload = Record<string, never>;
// Payload for onPushNotificationReceived callback (iOS only)
interface PushNotificationPayload {
title: string;
body: string;
}
// Payload for onLogReceived callback
interface LogReceivedPayload {
log: CrispLogEntry;
}
// Log entry from the native SDK
interface CrispLogEntry {
level: CrispLogLevel; // The log level
tag: string; // Log category/source
message: string; // Log message content
}
// Message origin type
type CrispMessageOrigin = "local" | "network" | "update";Types used within message content interfaces:
// Choice option for picker messages
interface PickerChoice {
value: string; // Unique identifier
label: string; // Display text
selected?: boolean; // Pre-selected state
}
// Target item for carousel messages
interface CarouselTarget {
title: string; // Item title
description?: string; // Item description
imageUrl?: string; // Image URL
actionUrl?: string; // Action URL when tapped
}| Value | Color | Suggested Use |
|---|---|---|
RED (0) |
Red | Errors, failures, critical events |
ORANGE (1) |
Orange | Warnings, attention needed |
YELLOW (2) |
Yellow | Informational highlights |
GREEN (3) |
Green | Success, completion, positive events |
BLUE (4) |
Blue | Informational, neutral events |
PURPLE (5) |
Purple | Special, premium-related events |
PINK (6) |
Pink | Social, engagement events |
BROWN (7) |
Brown | Historical, archive events |
GREY (8) |
Grey | Secondary, low-priority events |
BLACK (9) |
Black | System, administrative events |
| Value | Description |
|---|---|
VERBOSE (0) |
Most verbose; includes all log messages |
DEBUG (1) |
Debug information for development |
INFO (2) |
Informational messages |
WARN (3) |
Warnings (default level) |
ERROR (4) |
Error messages only |
ASSERT (5) |
Critical assertion failures |
The Crisp SDK requires native modules. Use a development build instead:
npx expo run:ios
# or
npx expo run:androidEnsure you've called configure() with a valid Website ID before calling show().
- Verify the config plugin is properly configured in
app.json - Rebuild with
npx expo prebuild --clean - Ensure your Crisp dashboard has push notifications enabled
- For iOS: Verify APNs certificates are configured in Crisp dashboard
- For Android: Verify Firebase is properly configured
Use setTokenId() with a unique user identifier to enable session persistence across app reinstalls and devices.
Two fully functional example apps are included in the repository to help you get started:
| App | Directory | Description |
|---|---|---|
| Expo | /example |
Expo Router app with push notifications (coexistence mode with expo-notifications), event listeners, and all SDK features |
| Bare React Native | /bare-example |
Bare React Native CLI app demonstrating integration without the Expo managed workflow |
Both apps are pre-configured and ready to run — just add your Website ID and follow the setup instructions in each directory.
- Crisp SDK Overview — All available Crisp SDKs
- React Native SDK Page — Product page for this SDK
- Crisp Help Center
- Crisp Developer Documentation
- iOS SDK Documentation
- Android SDK Documentation
Issues and pull requests are welcome on GitHub.
MIT - See LICENSE for details.
