crisp-sdk-react-native
v0.2.0
Published
Expo Module bridge for Crisp Chat iOS and Android SDK
Readme
[!WARNING] Minimum Expo SDK version
| SDK Version | Expo Compatibility | |---|---| | 0.1.5+ | Expo SDK 54+ (AGP 8.9.1+ required by Crisp Android SDK 2.0.16+) | | 0.1.4 | Expo SDK 51+ |
If you are on Expo 53 or older, pin to version 0.1.4:
npx expo install [email protected]
[!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:android
Installation
For Expo Apps
Install 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-nativeFor Bare React Native Apps
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.
Prerequisites
Before starting, ensure you have:
- React Native 0.74+
- iOS deployment target 15.1+
- Android minSdkVersion 24+
- Node.js 18+
Step 1: Install Expo Modules
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.
Step 2: Install Crisp SDK
# 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-nativeStep 3: Platform Configuration
iOS:
cd ios && pod installAndroid:
Ensure your android/app/build.gradle has:
android {
compileSdkVersion 36
defaultConfig {
minSdkVersion 24
targetSdkVersion 36
}
}Configuration
Get Your Website ID
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
Initialize Crisp
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
);
}Push Notifications (Config Plugin)
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"
}
}
]
]
}
}Plugin Options
| 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
websiteIdis required whennotifications.enabledistrue. The plugin will throw an error if it's missing.
What the Plugin Configures
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 --cleanfollowed bynpx expo run:iosornpx expo run:android.
Crisp Dashboard Configuration
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.
Coexistence Mode
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]
onPushNotificationReceivedis currently iOS only. On Android, the Crisp SDK does not expose a foreground notification callback — notifications are handled entirely at the nativeFirebaseMessagingServicelevel.
[!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.
Usage Examples
Open Chat
Display the Crisp chat widget:
import { show, openChat } from "crisp-sdk-react-native";
// Opens the widget (remembers the last active tab)
show();
// Always opens on the Chat tab, regardless of the last active tab
openChat();[!NOTE]
openChat()forces the Chat tab on iOS only. On Android, the native SDK always opens on the Chat tab by default, soopenChat()andshow()behave the same way.
User Identification
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();
}Session Data
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);Event Tracking
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 },
]);Event Listeners
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>
);
}Show Messages
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",
},
],
});Helpdesk
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
});Bot Scenarios
Trigger automated conversation flows:
import { runBotScenario } from "crisp-sdk-react-native";
// Start a bot scenario configured in your Crisp dashboard
runBotScenario("welcome-flow");Debug Logging
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 theonLogReceivedcallback.
API Reference
Configuration Methods
| 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 |
Logger Methods
| 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 |
User Information Methods
| 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 |
Session Data Methods
| 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> |
Event Tracking Methods
| 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 |
Session Management
| Method | Description | Parameters | Return |
| ---------------- | --------------------------------------------------------- | ---------- | ------ |
| resetSession() | Clear the current session and start a fresh conversation. | - | void |
UI Methods
| Method | Description | Parameters | Return |
| ------------------------------ | ------------------------------------------------------- | --------------------------------- | ------ |
| show() | Open the Crisp chat widget (last active tab). | - | void |
| openChat() | Open the Crisp chat widget always on the Chat tab (iOS only, see note below). | - | 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 |
Push Notification Methods (Coexistence Mode)
| 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 |
Message Methods
| Method | Description | Parameters | Return |
| ---------------------- | --------------------------------------------------- | ------------------------- | ------ |
| showMessage(content) | Display a message as operator in the local chatbox. | content: MessageContent | void |
React Hook
| Hook | Description | Parameters | Return |
| --------------------------- | ----------------------------------------------- | -------------------------------- | ------ |
| useCrispEvents(callbacks) | Subscribe to SDK events with automatic cleanup. | callbacks: CrispEventCallbacks | void |
Utility Functions
| Function | Description | Parameters | Return |
| ----------------- | ---------------------------------------------- | ---------- | -------- |
| getSDKVersion() | Get the crisp-sdk-react-native version string. | - | string |
TypeScript Types
Core Interfaces
Company
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
}Employment
interface Employment {
title?: string; // Job title (e.g., "Software Engineer")
role?: string; // Department/role (e.g., "Engineering")
}Geolocation
interface Geolocation {
country?: string; // Country name or ISO code
city?: string; // City name
}SessionEvent
interface SessionEvent {
name: string; // Event name
color: CrispSessionEventColors; // Event color
}Message Types
CrispMessage
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
}CrispUser
interface CrispUser {
nickname?: string; // Display name
userId?: string; // Unique identifier
avatar?: string; // Avatar URL
}Message Content Types
The showMessage method accepts these content types:
TextMessageContent
{ type: "text", text: string }FileMessageContent
{ type: "file", url: string, name: string, mimeType: string }AnimationMessageContent
{ type: "animation", url: string, mimeType: string }AudioMessageContent
{ type: "audio", url: string, mimeType: string, duration: number }PickerMessageContent
{
type: "picker",
id: string,
text: string,
choices: Array<{ value: string, label: string, selected?: boolean }>
}FieldMessageContent
{ type: "field", id: string, text: string, explain?: string, required?: boolean }CarouselMessageContent
{
type: "carousel",
text: string,
targets: Array<{
title: string,
description?: string,
imageUrl?: string,
actionUrl?: string
}>
}Event Callbacks
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;
}Event Payload Types
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";Helper Types
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
}Enums
CrispSessionEventColors
| 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 |
CrispLogLevel
| 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 |
Troubleshooting
"Expo Go is not supported"
The Crisp SDK requires native modules. Use a development build instead:
npx expo run:ios
# or
npx expo run:androidChat not appearing after show()
Ensure you've called configure() with a valid Website ID before calling show().
Push notifications not working
- 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
Session data not persisting
Use setTokenId() with a unique user identifier to enable session persistence across app reinstalls and devices.
Example Apps
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.
Resources
- 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
Contributing
Issues and pull requests are welcome on GitHub.
License
MIT - See LICENSE for details.
