A complete React Native SDK for Kaily AI chatbot integration with WebView-based AI functionality, dynamic tool registration, and comprehensive event handling.
- 🚀 Easy Integration - Simple initialization with minimal configuration
- 🛠️ Dynamic Tool Registration - Register custom tools that the AI can execute
- 📱 Cross-Platform - Works on both iOS and Android
- 🎨 Customizable UI - Fully customizable appearance and themes
- 🔊 Voice Support - Built-in voice input and text-to-speech capabilities
- 📡 Event System - Comprehensive event handling with EventEmitter
- 🔐 User Management - Built-in user authentication and context management
- 📦 TypeScript Support - Full TypeScript types for enhanced development
Current Version: 1.0.8
npm install @kailyai/react-native-sdkor
yarn add @kailyai/react-native-sdkNote: The SDK automatically installs its required dependencies (react-native-webview and eventemitter3).
- Install iOS native dependencies:
Since the SDK uses react-native-webview which requires native modules, you need to install iOS pods:
cd ios
pod install
cd ..- Add permissions to your
ios/YourApp/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for voice interactions with Kaily AI assistant.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app uses speech recognition for voice commands with Kaily AI assistant.</string>Add the following permissions to your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />That's it! The SDK automatically handles keyboard behavior on Android - no additional configuration needed.
import React, { useState, useEffect } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import {
KailySDK,
KailyWidget,
KailyTool,
KailyConfig,
} from '@kailyai/react-native-sdk';
export default function App() {
const [isInitialized, setIsInitialized] = useState(false);
const [showChat, setShowChat] = useState(false);
const sdk = KailySDK.getInstance();
useEffect(() => {
initializeSDK();
}, []);
const initializeSDK = async () => {
try {
const config: KailyConfig = {
token: 'your-kaily-token-here',
debugMode: true,
appearance: {
primaryColor: '#007AFF',
title: 'AI Assistant',
},
};
await sdk.initialize(config);
await registerTools();
setIsInitialized(true);
} catch (error) {
console.error('Failed to initialize:', error);
}
};
const registerTools = async () => {
const greetTool = new KailyTool({
name: 'greet_user',
description: 'Greet the user with a personalized message',
parameters: [
{
name: 'name',
type: 'string',
description: 'User name',
required: true,
},
],
handler: async (params) => {
return {
success: true,
data: { message: `Hello, ${params.name}!` },
};
},
});
await sdk.registerTool(greetTool);
};
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => setShowChat(!showChat)}>
<Text>Toggle Chat</Text>
</TouchableOpacity>
{showChat && isInitialized && (
<KailyWidget
config={sdk.currentConfig!}
bridge={sdk.getBridge()}
style={{ flex: 1 }}
/>
)}
</View>
);
}The SDK follows a singleton pattern. Initialize once in your app:
const sdk = KailySDK.getInstance();
const config: KailyConfig = {
token: 'your-token',
debugMode: true,
user: {
id: 'user_123',
name: 'John Doe',
email: 'john@example.com',
},
context: {
current_page: 'home',
user_tier: 'premium',
},
appearance: {
primaryColor: '#007AFF',
backgroundColor: '#FFFFFF',
title: 'My Assistant',
showHeader: true,
},
voiceConfig: {
enabled: true,
ttsEnabled: true,
language: 'en-US',
},
};
await sdk.initialize(config);Tools allow the AI to execute native functions:
const addToCartTool = new KailyTool({
name: 'add_to_cart',
description: 'Add a product to the shopping cart',
parameters: [
{
name: 'product_id',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'number',
defaultValue: 1,
},
],
handler: async (params) => {
// Your logic here
return {
success: true,
data: { cartCount: 5 },
};
},
});
// Register single tool
await sdk.registerTool(addToCartTool);
// Register multiple tools
await sdk.registerTools([tool1, tool2, tool3]);
// Unregister tool
await sdk.unregisterTool('add_to_cart');Listen to events from the AI:
const eventStream = sdk.getEventStream();
eventStream.on('event', (event: KailyEvent) => {
console.log('Event:', event.type, event.data);
});
// Listen to specific events
eventStream.on(KailyEventType.ConversationLoaded, (event) => {
console.log('Chat loaded!');
});
eventStream.on(KailyEventType.UserMessage, (event) => {
console.log('User said:', event.data?.message);
});
eventStream.on(KailyEventType.Error, (event) => {
console.error('Error:', event.data?.message);
});// Set user
await sdk.setUser({
id: 'user_123',
name: 'John Doe',
email: 'john@example.com',
attributes: {
tier: 'premium',
},
});
// Unset user
await sdk.unsetUser();
// Notify login success
await sdk.notifyLoginSuccess();
// Logout
await sdk.logout();Keep the AI informed about app state:
// Set context
await sdk.setContext({
current_page: 'checkout',
cart_count: 3,
cart_total: 149.99,
user_tier: 'premium',
});
// Update context when state changes
useEffect(() => {
if (sdk.isInitialized()) {
sdk.setContext({
current_page: pageName,
cart_count: cartItems.length,
});
}
}, [cartItems, pageName]);
// Unset context
await sdk.unsetContext();// Send a message programmatically
await sdk.sendMessage('What products do you recommend?');
// Send custom event
await sdk.sendGenericEvent('custom_event', {
action: 'button_clicked',
value: 'checkout',
});Control the Kaily UI programmatically:
// Toggle the context menu
await sdk.toggleContextMenu();
## API Reference
### KailySDK
Main SDK class (singleton):
```typescript
// Get instance
const sdk = KailySDK.getInstance();
// Initialize
await sdk.initialize(config: KailyConfig): Promise<void>
// Check if initialized
sdk.isInitialized(): boolean
// Tool management
await sdk.registerTool(tool: KailyTool): Promise<void>
await sdk.registerTools(tools: KailyTool[]): Promise<void>
await sdk.unregisterTool(toolName: string): Promise<void>
sdk.getRegisteredTools(): KailyTool[]
await sdk.addFrontendTools(tools: KailyTool[]): Promise<void>
await sdk.removeFrontendTools(toolNames: string[]): Promise<void>
await sdk.removeAllTools(): Promise<void>
// User management
await sdk.setUser(user: KailyUser): Promise<void>
await sdk.unsetUser(): Promise<void>
await sdk.notifyLoginSuccess(): Promise<void>
await sdk.logout(): Promise<void>
// Context management
await sdk.setContext(context: Record<string, any>): Promise<void>
await sdk.unsetContext(): Promise<void>
// Messaging
await sdk.sendMessage(message: string): Promise<void>
await sdk.sendEvent(event: KailyEvent): Promise<void>
await sdk.sendGenericEvent(eventType: string, data: Record<string, any>): Promise<void>
// Event streams
sdk.getEventStream(): EventEmitter
sdk.getToolCallStream(): EventEmitter
// Getters
sdk.currentConfig: KailyConfig | null
sdk.currentUser: KailyUser | null
sdk.currentContext: Record<string, any> | null
sdk.hasUser: boolean
// Lifecycle
sdk.dispose(): void
// UI Actions
await sdk.toggleContextMenu(): Promise<void>
React component for displaying the chat interface:
<KailyWidget
config={config}
bridge={sdk.getBridge()}
onConversationLoaded={() => console.log('Loaded')}
onConversationFailedToLoad={(error) => console.error(error)}
onDeepLinkReceived={(url) => console.log(url)}
onTelemetryEvent={(type, data) => console.log(type, data)}
onClose={() => setShowChat(false)}
onEvent={(event) => console.log(event)}
style={{ flex: 1 }}
/>interface KailyConfig {
token: string; // Required
baseUrl?: string;
widgetUrl?: string;
user?: KailyUser;
appearance?: KailyAppearance;
voiceConfig?: KailyVoiceConfig;
debugMode?: boolean;
enableTelemetry?: boolean;
context?: Record<string, any>;
headers?: Record<string, string>;
loadTimeoutMs?: number;
enableJsDebugging?: boolean;
userAgent?: string;
allowExternalNavigation?: boolean;
callbackUrls?: string[];
initParams?: Record<string, any>;
}interface KailyUser {
id: string; // Required
name?: string;
email?: string;
phone?: string;
avatarUrl?: string;
attributes?: Record<string, any>;
session?: string;
}interface KailyAppearance {
primaryColor?: string;
backgroundColor?: string;
textColor?: string;
secondaryTextColor?: string;
userMessageColor?: string;
botMessageColor?: string;
borderRadius?: number;
customCss?: string;
fontFamily?: string;
fontSize?: number;
title?: string;
subtitle?: string;
logoUrl?: string;
showHeader?: boolean;
showTimestamps?: boolean;
darkMode?: boolean;
themeMode?: 'light' | 'dark' | 'auto';
}enum KailyEventType {
ConversationLoaded = 'conversationLoaded',
ConversationFailedToLoad = 'conversationFailedToLoad',
DeepLinkReceived = 'deepLinkReceived',
Telemetry = 'telemetry',
Close = 'close',
ToolCall = 'toolCall',
ToolResult = 'toolResult',
UserMessage = 'userMessage',
BotMessage = 'botMessage',
Error = 'error',
AuthChanged = 'authChanged',
ContextUpdated = 'contextUpdated',
VoiceInputStarted = 'voiceInputStarted',
VoiceInputStopped = 'voiceInputStopped',
TtsStarted = 'ttsStarted',
TtsCompleted = 'ttsCompleted',
WidgetResized = 'widgetResized',
Navigation = 'navigation',
Custom = 'custom',
}import {
KailyException,
KailyInitializationException,
KailyConfigurationException,
KailyToolException,
KailyWebViewException,
} from '@kailyai/react-native-sdk';
try {
await sdk.initialize(config);
} catch (error) {
if (error instanceof KailyConfigurationException) {
console.error('Configuration error:', error.field, error.message);
} else if (error instanceof KailyInitializationException) {
console.error('Initialization error:', error.message);
} else {
console.error('Unknown error:', error);
}
}Request microphone permission for voice features:
import { PermissionHelper } from '@kailyai/react-native-sdk';
const granted = await PermissionHelper.requestMicrophonePermission();
if (!granted) {
Alert.alert('Permission Required', 'Microphone permission is needed for voice features', [
{ text: 'Cancel' },
{ text: 'Open Settings', onPress: () => PermissionHelper.openAppSettings() },
]);
}See the example directory for a comprehensive demo app that showcases:
- SDK initialization with error handling
- Shopping cart with multiple tools
- Event logging
- Context updates
- User management
- Full UI implementation
To run the example:
cd example
yarn install
# iOS
cd ios && pod install && cd ..
yarn ios
# Android
yarn android- Ensure you have
react-native-webviewinstalled - Check your network connection
- Verify your Kaily token is valid
- Enable
debugMode: trueto see detailed logs
- Ensure SDK is initialized before registering tools
- Check tool handler returns proper
KailyToolResultformat - Enable debug mode to see tool call logs
- Verify tool names match between registration and AI calls
- Ensure you subscribe to events after SDK initialization
- Check you're listening to the correct event types
- Verify WebView has loaded successfully
The SDK automatically handles keyboard behavior on Android using React Native's Keyboard API. No AndroidManifest configuration needed! If you still experience issues:
- Ensure you're using the latest version of the SDK
- Check that the KailyWidget has proper flex styling:
style={{ flex: 1 }} - Verify there are no conflicting
KeyboardAvoidingViewwrappers in your app
The SDK automatically handles deep link interception on Android. No additional configuration needed. If links are still redirecting:
- Ensure you're using the latest version of the SDK
- Check that the
onDeepLinkReceivedcallback is set on your KailyWidget - Enable debug mode and check console logs for interception messages
This SDK is written in TypeScript and includes full type definitions. No additional @types packages needed.
MIT
For issues, questions, or contributions, please visit our GitHub repository.