npm package: @revrag-ai/embed-react-native
Description: A powerful React Native library for integrating AI-powered voice agents into mobile applications. Features real-time voice communication, intelligent speech processing, customizable UI components, and comprehensive event handling for building conversation
Author: RevragAi
Latest version: 1.0.34

# Embed React Native SDK Integration Guide

## Overview

The Embed React Native SDK is a powerful voice-enabled AI agent library that provides real-time communication capabilities. This comprehensive guide will walk you through the complete integration process from installation to deployment.

## Table of Contents

1. [Installation](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
2. [Peer Dependencies](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
3. [Android Configuration](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
4. [iOS Configuration](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
5. [Babel Configuration](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
6. [SDK Initialization](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
7. [App Setup](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
8. [Event System](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
9. [Usage Examples](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
10. [FAQ & Troubleshooting](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)

## Installation

Install the Embed React Native SDK using your preferred package manager:

```bash
# Using npm
npm install @revrag-ai/embed-react-native

# Using yarn
yarn add @revrag-ai/embed-react-native

# Using pnpm
pnpm add @revrag-ai/embed-react-native

```

## Peer Dependencies

The SDK requires several peer dependencies to be installed in your project. Install all required dependencies:

```bash
# Install peer dependencies
npm install @livekit/react-native @livekit/react-native-webrtc
npm install @react-native-async-storage/async-storage
npm install react-native-gesture-handler react-native-reanimated
npm install react-native-linear-gradient lottie-react-native
npm install react-native-safe-area-context

# For iOS, run pod install
cd ios && pod install && cd ..

# For iOS: register bundled fonts (run once from your app root)
npx react-native-asset

```

**Bundled fonts:** The SDK ships with fonts that work automatically on Android. On iOS, run `npx react-native-asset` once from your app root after installing so the fonts are registered in your app.

### Alternative installation commands:

**Using Yarn:**

```bash
yarn add @livekit/react-native @livekit/react-native-webrtc @react-native-async-storage/async-storage react-native-gesture-handler react-native-reanimated react-native-linear-gradient lottie-react-native react-native-safe-area-context

```

**Using pnpm:**

```bash
pnpm add @livekit/react-native @livekit/react-native-webrtc @react-native-async-storage/async-storage react-native-gesture-handler react-native-reanimated react-native-linear-gradient lottie-react-native react-native-safe-area-context

```

## Android Configuration

### 1. Android Manifest Permissions

Add the following permissions to your `android/app/src/main/AndroidManifest.xml`:

```xml
<manifest xmlns:android="<http://schemas.android.com/apk/res/android>">

    <!-- Required permissions for Embed SDK -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.MICROPHONE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application
        android:name=".MainApplication"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:allowBackup="false"
        android:theme="@style/AppTheme"
        android:supportsRtl="true"
        android:usesCleartextTraffic="true"
        android:hardwareAccelerated="true">

        <!-- Your activities and other components -->

    </application>
</manifest>

```

### 2. Build.gradle Configuration

Add Lottie dependency to your `android/app/build.gradle`:

```
dependencies {
    implementation 'com.airbnb.android:lottie:6.0.1'
    // ... other dependencies
}

```

### 3. ProGuard Configuration (if using ProGuard)

Add to your `android/app/proguard-rules.pro`:

```
# Embed SDK
-keep class com.revrag.embed.** { *; }
-keep class org.webrtc.** { *; }
-dontwarn org.webrtc.**

# Lottie
-keep class com.airbnb.lottie.** { *; }

```

## iOS Configuration

### 1. iOS Permissions

**🚨 CRITICAL:** Add the following permissions to your `ios/YourAppName/Info.plist`. **Missing `NSMicrophoneUsageDescription` will cause the app to crash when accessing the microphone:**

```xml
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice communication with AI agent</string>

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

```

**⚠️ App Crash Fix:** If your app crashes with "attempted to access privacy-sensitive data without a usage description", ensure the `NSMicrophoneUsageDescription` key is present in your `Info.plist`.

### 2. Pod Installation

After installing peer dependencies, run:

```bash
cd ios
pod install
cd ..

```

### 3. iOS Build Settings (if needed)

If you encounter build issues, add these to your iOS project settings:

- Enable Bitcode: `NO`
- Build Active Architecture Only: `YES` (for Debug)

## Babel Configuration

**⚠️ CRITICAL: React Native Reanimated Configuration**

Add the React Native Reanimated plugin to your `babel.config.js`. **This must be the last plugin in the plugins array:**

```jsx
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    // ... other plugins
    'react-native-reanimated/plugin', // ← This MUST be the last plugin
  ],
};

```

**❌ Common Mistake:**

```jsx
// DON'T DO THIS - other plugins after reanimated plugin will cause issues
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    'react-native-reanimated/plugin',
    'some-other-plugin', // ← This will break reanimated
  ],
};

```

**✅ Correct Configuration:**

```jsx
// DO THIS - reanimated plugin as the last plugin
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    'some-other-plugin',
    'another-plugin',
    'react-native-reanimated/plugin', // ← Last plugin
  ],
};

```

After updating `babel.config.js`, clean your project:

```bash
# For React Native
npx react-native start --reset-cache

# For Expo (if applicable)
expo start --clear

```

## SDK Initialization

### 1. useInitialize Hook

Initialize the SDK at the root level of your application using the `useInitialize` hook:

```jsx
import { useInitialize } from '@revrag-ai/embed-react-native';

function App() {
  const { isInitialized, error } = useInitialize({
    apiKey: 'YOUR_API_KEY',
    embedUrl: 'YOUR_ONWID_SERVER_URL',
  });

  if (error) {
    console.error('SDK initialization failed:', error);
  }

  if (!isInitialized) {
    // Show loading screen while initializing
    return <LoadingScreen />;
  }

  // Your app components
  return <YourApp />;
}

```

### 2. Configuration Options

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `apiKey` | string | ✅ | Your Embed API key |
| `embedUrl` | string | ✅ | Your Embed server URL |

## App Setup

### 1. Wrap App with GestureHandlerRootView

**⚠️ IMPORTANT:** You must wrap your entire app with `GestureHandlerRootView` for the SDK to work properly:

```jsx
import React from 'react';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { useInitialize } from '@revrag-ai/embed-react-native';

export default function App() {
  const { isInitialized, error } = useInitialize({
    apiKey: 'your_api_key_here',
    embedUrl: '<https://your-embed-server.com>',
  });

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      {/* Your app components */}
    </GestureHandlerRootView>
  );
}

```

### 2. Add EmbedButton Component

Add the floating voice agent button to your screen:

```jsx
import { EmbedButton } from '@revrag-ai/embed-react-native';

function MyScreen() {
  return (
    <View style={{ flex: 1 }}>
      {/* Your screen content */}
      <EmbedButton />
    </View>
  );
}

```

## Event System

The SDK provides a powerful event system for sending user context and application state to the AI agent.

### Available Events

The SDK exports the following event types:

```jsx
import { EventKeys } from '@revrag-ai/embed-react-native';

// Available event keys:
EventKeys.USER_DATA    // 'user_data' - User identity and profile
EventKeys.SCREEN_STATE // 'state_data' - Application state and context

```

### Event Usage Rules

**🚨 CRITICAL REQUIREMENT:**

1. **USER_DATA event MUST be sent first** before any other events
2. **USER_DATA must include `app_user_id`** for user identification
3. **EmbedButton should only be rendered AFTER** USER_DATA event is sent
4. **SCREEN_STATE events** can only be sent after USER_DATA is established

### Event Methods

```jsx
import { embed, EventKeys } from '@revrag-ai/embed-react-native';

// Send events
await embed.Event(eventKey, data);

// Listen to events (optional)
embed.on(eventKey, callback);

```

### How Events are Triggered

Events are triggered using the `embed.Event()` method and automatically:

1. **Validate the event key** against allowed EventKeys
2. **Store user identity** from USER_DATA events
3. **Auto-append app_user_id** to subsequent events
4. **Send data to your server** via the configured API
5. **Trigger local event listeners**

```jsx
// Example event flow
try {
  // Step 1: Send user data first (required)
  await embed.Event(EventKeys.USER_DATA, {
    app_user_id: 'user123',
    name: 'John Doe',
    email: 'john@example.com',
  });

  // Step 2: Send context data (app_user_id auto-added)
  await embed.Event(EventKeys.SCREEN_STATE, {
    screen: 'profile',
    data: { plan: 'premium' },
  });
} catch (error) {
  console.error('Event error:', error);
}

```

## Usage Examples

### Complete Integration Example

```jsx
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Alert } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import {
  useInitialize,
  EmbedButton,
  embed,
  EventKeys
} from '@revrag-ai/embed-react-native';

export default function App() {
  const [userDataSent, setUserDataSent] = useState(false);

  const { isInitialized, error } = useInitialize({
    apiKey: 'your_api_key_here',
    embedUrl: '<https://your-embed-server.com>',
  });

  // Initialize user data when SDK is ready
  useEffect(() => {
    if (isInitialized && !userDataSent) {
      initializeUserData();
    }
  }, [isInitialized, userDataSent]);

  const initializeUserData = async () => {
    try {
      // STEP 1: Send user data first (REQUIRED)
      await embed.Event(EventKeys.USER_DATA, {
        app_user_id: 'user123', // Required field
        name: 'John Doe',
        email: 'john@example.com',
        subscription: 'premium',
        joinedDate: '2024-01-15',
      });

      setUserDataSent(true);

      // STEP 2: Send initial screen state
      await embed.Event(EventKeys.SCREEN_STATE, {
        screen: 'home',
        timestamp: new Date().toISOString(),
        userActions: [],
      });

      console.log('User data and initial state sent successfully');
    } catch (error) {
      console.error('Failed to initialize user data:', error);
      Alert.alert('Error', 'Failed to initialize voice agent');
    }
  };

  // Send screen state updates
  const updateScreenState = async (screenName, data = {}) => {
    if (!userDataSent) {
      console.warn('Cannot send screen state before user data');
      return;
    }

    try {
      await embed.Event(EventKeys.SCREEN_STATE, {
        screen: screenName,
        timestamp: new Date().toISOString(),
        ...data,
      });
    } catch (error) {
      console.error('Failed to update screen state:', error);
    }
  };

  // Handle initialization errors
  if (error) {
    console.error('SDK initialization failed:', error);
    return (
      <View style={styles.errorContainer}>
        <Text>Failed to initialize voice agent</Text>
      </View>
    );
  }

  // Show loading while initializing
  if (!isInitialized) {
    return (
      <View style={styles.loadingContainer}>
        <Text>Initializing voice agent...</Text>
      </View>
    );
  }

  return (
    <GestureHandlerRootView style={styles.container}>
      <View style={styles.content}>
        {/* Your app content */}
        <YourAppComponents onScreenChange={updateScreenState} />
      </View>

      {/* Only render EmbedButton after user data is sent */}
      {userDataSent && <EmbedButton />}
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  content: {
    flex: 1,
  },
  loadingContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  errorContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

```

### Event Listening Example

```jsx
import { embed, EventKeys } from '@revrag-ai/embed-react-native';

// Listen for events (optional)
useEffect(() => {
  // Listen for user data events
  const unsubscribeUserData = embed.on(EventKeys.USER_DATA, (data) => {
    console.log('User data updated:', data);
  });

  // Listen for screen state events
  const unsubscribeScreenState = embed.on(EventKeys.SCREEN_STATE, (data) => {
    console.log('Screen state changed:', data);
  });

  // Cleanup listeners
  return () => {
    // Note: Current version doesn't provide unsubscribe method
    // This is for illustration of the API design
  };
}, []);

```

### Advanced Usage with Navigation

```jsx
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';
import { embed, EventKeys } from '@revrag-ai/embed-react-native';

function NavigationListener() {
  const navigation = useNavigation();

  useEffect(() => {
    const unsubscribe = navigation.addListener('state', (e) => {
      const routeName = e.data.state.routes[e.data.state.index].name;

      // Send screen state when navigation changes
      embed.Event(EventKeys.SCREEN_STATE, {
        screen: routeName,
        timestamp: new Date().toISOString(),
        navigationStack: e.data.state.routes.map(route => route.name),
      }).catch(console.error);
    });

    return unsubscribe;
  }, [navigation]);

  return null;
}

```

## FAQ & Troubleshooting

### 🔴 Critical Issues

### Q: "react-native-reanimated not working" or animation issues

**A:** This is the most common issue. Ensure:

1. ✅ React Native Reanimated plugin is **the last plugin** in `babel.config.js`
2. ✅ Clear cache after babel config changes: `npx react-native start --reset-cache`
3. ✅ Restart Metro bundler completely
4. ✅ For iOS: `cd ios && pod install`

```jsx
// ✅ Correct babel.config.js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    'react-native-reanimated/plugin', // ← MUST be last
  ],
};

```

### Q: "User identity not found" error

**A:** This error occurs when you try to send events before USER_DATA:

1. ✅ Send `USER_DATA` event first with `app_user_id`
2. ✅ Wait for the event to complete before sending other events
3. ✅ Only render `EmbedButton` after USER_DATA is sent

```jsx
// ❌ Wrong order
await embed.Event(EventKeys.SCREEN_STATE, { screen: 'home' }); // Error!
await embed.Event(EventKeys.USER_DATA, { app_user_id: 'user123' });

// ✅ Correct order
await embed.Event(EventKeys.USER_DATA, { app_user_id: 'user123' });
await embed.Event(EventKeys.SCREEN_STATE, { screen: 'home' }); // Works!

```

### Q: Microphone permission denied

**A:** Ensure permissions are configured:

**Android:**

- ✅ `RECORD_AUDIO` and `MICROPHONE` permissions in `AndroidManifest.xml`
- ✅ Request permissions at runtime for Android 6+

**iOS:**

- ✅ `NSMicrophoneUsageDescription` in `Info.plist`
- ✅ Provide user-friendly description

### Q: iOS App Crashes - "attempted to access privacy-sensitive data without a usage description"

**A:** This crash occurs when the app tries to access the microphone without proper permission description:

**Quick Fix:**

1. ✅ Open `ios/YourAppName/Info.plist`
2. ✅ Add the microphone usage description:

```xml
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice communication with AI agent</string>

```

1. ✅ Clean and rebuild: `cd ios && pod install && cd .. && npx react-native run-ios`

**Why this happens:** iOS requires apps to declare why they need access to privacy-sensitive data like microphone, camera, location, etc.

### 🟡 Common Issues

### Q: EmbedButton not appearing

**A:** Check these requirements:

1. ✅ App wrapped with `GestureHandlerRootView`
2. ✅ SDK initialized successfully (`isInitialized` is true)
3. ✅ USER_DATA event sent first
4. ✅ EmbedButton rendered after USER_DATA

### Q: Network/API connection issues

**A:** Verify configuration:

1. ✅ Valid `apiKey` and `embedUrl`
2. ✅ Network connectivity
3. ✅ Server is accessible from the device
4. ✅ `usesCleartextTraffic="true"` for HTTP endpoints (Android)

### Q: iOS Network Request Failures - "The resource could not be loaded"

**A:** This is caused by iOS App Transport Security (ATS). Solutions:

**For HTTP APIs (Development/Testing):**
Add domain exceptions to `ios/YourApp/Info.plist`:

```xml
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
    <key>NSAllowsLocalNetworking</key>
    <true/>
    <key>NSExceptionDomains</key>
    <dict>
        <!-- Replace with your API domain -->
        <key>your-api-domain.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSExceptionMinimumTLSVersion</key>
            <string>TLSv1.0</string>
            <key>NSExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>
        <!-- For localhost development -->
        <key>localhost</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

```

**For Production (Recommended):**

1. ✅ Use HTTPS endpoints instead of HTTP
2. ✅ Get proper SSL certificates
3. ✅ Update `embedUrl` to use `https://`

**⚠️ Never use `NSAllowsArbitraryLoads: true` in production apps**

### Q: Network debugging on iOS

**A:** Enable network debugging:

1. **Add logging to API calls:**

```jsx
// Add this to your API initialization
console.log('API URL:', embedUrl);
console.log('Making request to:', `${embedUrl}/embedded-agent/initialize`);

```

1. **Check iOS Console logs:**
    - Open Xcode → Window → Devices and Simulators
    - Select your device → Open Console
    - Look for network-related errors
2. **Test network connectivity:**

```bash
# Test if your API is reachable
curl -I <http://your-api-domain.com/embedded-agent/initialize>

```

### 🟢 Best Practices

### Q: How to handle SDK initialization in different app states?

**A:** Best practices for initialization:

```jsx
const [initState, setInitState] = useState('loading');

const { isInitialized, error } = useInitialize({
  apiKey: process.env.ONWID_API_KEY,
  embedUrl: process.env.ONWID_URL,
});

useEffect(() => {
  if (error) {
    setInitState('error');
  } else if (isInitialized) {
    setInitState('ready');
  }
}, [isInitialized, error]);

// Render based on state
switch (initState) {
  case 'loading': return <LoadingScreen />;
  case 'error': return <ErrorScreen error={error} />;
  case 'ready': return <AppWithOnwid />;
}

```

### Q: How to optimize event sending?

**A:** Event optimization strategies:

```jsx
// ✅ Debounce frequent events
const debouncedStateUpdate = useCallback(
  debounce((data) => {
    embed.Event(EventKeys.SCREEN_STATE, data);
  }, 500),
  []
);

// ✅ Batch related data
const sendUserProfile = async (userData) => {
  await embed.Event(EventKeys.USER_DATA, {
    app_user_id: userData.id,
    ...userData.profile,
    ...userData.preferences,
    lastLogin: new Date().toISOString(),
  });
};

```

### Q: How to handle offline scenarios?

**A:** Offline handling approach:

```jsx
import NetInfo from '@react-native-community/netinfo';

const [isOnline, setIsOnline] = useState(true);

useEffect(() => {
  const unsubscribe = NetInfo.addEventListener(state => {
    setIsOnline(state.isConnected);
  });
  return () => unsubscribe();
}, []);

// Queue events when offline
const sendEventWithRetry = async (eventKey, data) => {
  if (!isOnline) {
    // Store in AsyncStorage for later retry
    await storeEventForRetry(eventKey, data);
    return;
  }

  try {
    await embed.Event(eventKey, data);
  } catch (error) {
    // Retry logic or store for later
    await storeEventForRetry(eventKey, data);
  }
};

```

### 📚 Additional Documentation

- [Complete API Reference (API.md)](API.md)
- [Detailed Examples (EXAMPLES.md)](EXAMPLES.md)
- [Migration Guide (MIGRATION_GUIDE.md)](MIGRATION_GUIDE.md)
- [Quick Start Guide (QUICK_START.md)](QUICK_START.md)

#### Troubleshooting Guides

- **[Ping Timeout Warning (PING_TIMEOUT_GUIDE.md)](PING_TIMEOUT_GUIDE.md)** - Understanding LiveKit connection warnings
- **[Troubleshooting Summary (TROUBLESHOOTING_SUMMARY.md)](TROUBLESHOOTING_SUMMARY.md)** - Quick fixes for common issues
- [LiveKit Setup (LIVEKIT_SETUP.md)](LIVEKIT_SETUP.md) - Native configuration guide
- [Event System Debug (DEBUG_EVENT_LISTENERS.md)](DEBUG_EVENT_LISTENERS.md) - Event handling troubleshooting

### 📞 Support

For additional help:

- **Documentation:** [https://docs.revrag.ai](https://docs.revrag.ai/)
- **Email Support:** [contact@revrag.ai](mailto:contact@revrag.ai)
- **GitHub Issues:** https://github.com/RevRag-ai/embed-react-native/issues

### 🔄 Migration Guide

If upgrading from a previous version, check the [CHANGELOG.md](https://www.notion.so/CHANGELOG.md) for breaking changes and migration steps.

---

**Last Updated:** January 2024
**SDK Version:** Latest
**React Native Compatibility:** 0.70+