> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revrag.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> React SDK Integration Guide - Voice-enabled AI agent with real-time communication capabilities

# 🚀 Complete Integration Guide

## 📦 Installation

```bash theme={null}
npm install @revrag-ai/embed-react
# or
yarn add @revrag-ai/embed-react
```

## ⚠️ Important: CSS Import (REQUIRED)

**The CSS file MUST be imported** for the widget to display correctly. Without it, the widget will appear unstyled.

### Option 1: Global Import (Recommended)

**React/Vite:**

```tsx theme={null}
// src/main.tsx or src/index.tsx
import '@revrag-ai/embed-react/style.css';
import App from './App';
import ReactDOM from 'react-dom/client';

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

**Next.js App Router:**

```tsx theme={null}
// app/layout.tsx
import '@revrag-ai/embed-react/style.css';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

**Next.js Pages Router:**

```tsx theme={null}
// pages/_app.tsx
import '@revrag-ai/embed-react/style.css';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}
```

### Option 2: Component-Level Import

```tsx theme={null}
// YourComponent.tsx
import { EmbedButton, useInitialize } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/style.css';
```

### Option 3: CSS File Import

```css theme={null}
/* In your global styles.css */
@import '@revrag-ai/embed-react/style.css';
```

***

## 🎯 Basic Usage

### Fixed Positioning (Default)

```tsx theme={null}
import { EmbedButton, useInitialize } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/style.css';

function App() {
  const { isInitialized } = useInitialize("your-api-key");

  if (!isInitialized) {
    return <div>Loading AI Assistant...</div>;
  }

  return (
    <div>
      <h1>My Application</h1>
      {/* Widget will appear at bottom-right corner */}
      <EmbedButton positioning="fixed" />
    </div>
  );
}
```

### Embedded Positioning

```tsx theme={null}
import { EmbedButton, useInitialize } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/style.css';

function HelpSection() {
  const { isInitialized } = useInitialize("your-api-key");

  if (!isInitialized) return null;

  return (
    <div style={{ 
      position: 'relative', 
      height: '500px',
      background: '#f5f5f5',
      borderRadius: '12px'
    }}>
      <h2>Need Help?</h2>
      <p>Chat with our AI assistant</p>
      
      {/* Widget will appear at bottom-left of this container */}
      <EmbedButton 
        positioning="embedded" 
        side="left"
      />
    </div>
  );
}
```

***

## 🧩 EmbedProvider (Recommended)

Instead of manually placing `<EmbedButton />` on every page, wrap your app once with `EmbedProvider` and let it handle widget visibility automatically.

<Tip>
  **BEST PRACTICE:** Use `EmbedProvider` to automatically manage the EmbedButton based on the current route.

  The `EmbedProvider` component:

  * ✅ **Automatically tracks** route changes and shows/hides the widget
  * ✅ **Conditionally renders** the EmbedButton based on the current page
  * ✅ **Supports delays** before the button appears on a route
  * ✅ **Supports visibility groups** for per-route delay, continuity, and position
  * ✅ **Built-in component** - no need to create a custom provider
</Tip>

### Basic Setup

```tsx theme={null}
import { EmbedProvider, useInitialize } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/style.css';

function App() {
  const { isInitialized } = useInitialize("your-api-key");

  if (!isInitialized) return <div>Loading...</div>;

  return (
    <EmbedProvider appVersion="1.0.0">
      {/* Your app / router */}
    </EmbedProvider>
  );
}
```

### Show on specific routes only

```tsx theme={null}
<EmbedProvider
  appVersion="1.0.0"
  includeScreens={['/home', '/dashboard', '/profile']}
>
  {/* Widget only visible on the listed routes */}
</EmbedProvider>
```

### EmbedProvider Props

| Property                      | Type                                   | Required | Description                                                  |
| ----------------------------- | -------------------------------------- | -------- | ------------------------------------------------------------ |
| `children`                    | ReactNode                              | ✅        | Your app or router                                           |
| `appVersion`                  | string                                 | ✅        | App version string used in analytics                         |
| `includeScreens`              | string\[]                              | ❌        | Routes where the button appears. Omit to show on all routes  |
| `matchMode`                   | 'exact' \| 'prefix'                    | ❌        | Route matching mode (default: 'exact')                       |
| `currentPath`                 | string                                 | ❌        | Current route path (auto-detected if not provided)           |
| `usePathHook`                 | () => string                           | ❌        | Custom hook for path detection (e.g., Next.js `usePathname`) |
| `embedButtonDelayMs`          | number                                 | ❌        | Delay (ms) before the button appears on a route              |
| `embedButtonVisibilityConfig` | EmbedButtonVisibilityConfig            | ❌        | Advanced per-group delay, continuity, and inset config       |
| `embedButtonPosition`         | 'top' \| 'bottom' \| 'left' \| 'right' | ❌        | Button position for fixed positioning                        |

<a href="/embed/integration/embed-react-provider-advanced" target="_blank" rel="noreferrer">
  Advanced usage guide →
</a>

***

## 🎨 Responsive Behavior

### Fixed Positioning

* **Desktop (> 500px)**: Widget stays at specified position
* **Mobile (≤ 500px)**: Widget expands to full width with 1rem padding from edges

### Embedded Positioning

* **Wide parent (> 400px)**: Widget aligns to left/right based on `side` prop
* **Narrow parent (≤ 400px)**: Widget auto-centers with equal padding

***

## 🔧 API Reference

### EmbedButton Props

```tsx theme={null}
interface EmbedButtonProps {
  // Positioning mode
  positioning?: 'fixed' | 'embedded';  // default: 'fixed'
  
  // Position configuration (for fixed mode)
  position?: {
    top?: string;
    bottom?: string;
    left?: string;
    right?: string;
  };
  
  // Easy positioning (for embedded mode)
  side?: 'left' | 'right';
  
  // Offset from bottom (useful for bottom navbars)
  bottomOffset?: number;  // in pixels
  
  // Custom className
  className?: string;
}
```

### Hooks

#### useInitialize

```tsx theme={null}
const { isInitialized, error } = useInitialize(apiKey: string);
```

Initializes the SDK with your API key. Must be called before using any other SDK features.

#### useEmbed

```tsx theme={null}
const { currentPath } = useEmbed();
```

Reads `currentPath` from `EmbedProvider` context. Useful for accessing the current route within your components.

#### useLiveKit

```tsx theme={null}
const { connect, disconnect, toggleMute } = useLiveKit();
```

Provides programmatic control over voice calls:

* `connect()`: Initiate a voice call
* `disconnect()`: End the current voice call
* `toggleMute()`: Mute/unmute the microphone

#### useSSRSafe / useBrowserSafe

```tsx theme={null}
const isMounted = useSSRSafe();
// or
const isMounted = useBrowserSafe();
```

SSR-safe mount detection hooks. Returns `true` when component is mounted on the client side. Useful for Next.js and other SSR frameworks to prevent hydration mismatches.

***

## 📡 Event Management

The SDK provides a powerful event system for tracking user data, custom events, and listening to agent state changes.

### EventKeys

Available event types:

```tsx theme={null}
import { EventKeys } from '@revrag-ai/embed-react';

EventKeys.USER_DATA          // 'user_data' - User identification and profile data
EventKeys.CUSTOM_EVENT       // 'custom_event' - Custom application events
EventKeys.AGENT_CONNECTED    // 'agent_start' - Voice agent connection (auto-tracked)
EventKeys.AGENT_DISCONNECTED // 'agent_end' - Voice agent disconnection (auto-tracked)
EventKeys.ANALYTICS_DATA     // 'analytics_data' 
```

**Note**: Only `USER_DATA` , `CUSTOM_EVENT` and `ANALYTICS_DATA` are available for manual use. Agent connection events are automatically tracked by the SDK and can be listened to via callbacks.

***

### Sending Events with embedEvent API

The `embedEvent` object provides methods for sending events to track user data and custom application events.

#### Send User Data

```tsx theme={null}
import { embedEvent, EventKeys } from '@revrag-ai/embed-react';

// Send user data
const response = await embedEvent.event({
  eventKey: EventKeys.USER_DATA,
  data: {
    app_user_id: 'user-123',
    email: 'user@example.com',
    name: 'John Doe',
    plan: 'premium'
  }
});

if (response.success) {
  console.log('User data sent successfully');
}
```

#### Send Custom Events

```tsx theme={null}
import { embedEvent, EventKeys } from '@revrag-ai/embed-react';

// Track custom application event
await embedEvent.event({
  eventKey: EventKeys.CUSTOM_EVENT,
  data: {
    event_name: 'purchase_completed',
    product_id: 'prod-123',
    amount: 99.99
  }
});
```

#### Send Analytics Events

```tsx theme={null}
import { embedEvent, EventKeys } from '@revrag-ai/embed-react';

// Track custom application event
await embedEvent.event({
  eventKey: EventKeys.ANALYTICS_DATA,
  data: {
    event_name: 'purchase_completed',  // event_name is compulsory in analytics_data event
    product_id: 'prod-123',
    amount: 99.99
  }

});
```

#### Event Method Signature

```tsx theme={null}
embedEvent.event(params: UpdateDataRequest): Promise<ApiResponse>

interface UpdateDataRequest {
  eventKey: EventKey;           // Event type from EventKeys
  data: {
    app_user_id?: string;       // User ID (required for USER_DATA)
    [key: string]: unknown;     // Additional data
  };
  session_id?: string;          // Optional session ID
}

interface ApiResponse {
  success: boolean;
  data?: unknown;
  message?: string;
  error?: string;
}
```

***

### Listening to Agent Events

Monitor voice agent connection status in real-time. **These events are automatically sent to your backend AND emitted locally** for you to listen to.

#### Available Event Types for Listening

```tsx theme={null}
import { EventKeys } from '@revrag-ai/embed-react';

// Available events for listening:
EventKeys.AGENT_CONNECTED        // 'agent_start' - Voice agent connected
EventKeys.AGENT_DISCONNECTED     // 'agent_end' - Voice agent disconnected
```

**Automatic Backend Sync:**

* Agent events are **automatically sent to your backend** with `app_user_id`
* Events are **also emitted locally** for real-time UI updates
* Backend receives all event data including timestamps and metadata
* No manual API calls needed - it's all handled automatically

#### Event Listener Methods

```tsx theme={null}
import { embedEvent } from '@revrag-ai/embed-react';

// Add event listener
embedEvent.addCallback(callback);

// Remove event listener
embedEvent.removeCallback(callback);
```

#### Basic Event Listening Example

```tsx theme={null}
import React, { useEffect } from 'react';
import { embedEvent, EventKeys, EmbedButton } from '@revrag-ai/embed-react';

function MyComponent() {
  useEffect(() => {
    // Define event handler
    const handleAgentEvent = (event) => {
      if (event.type === EventKeys.AGENT_CONNECTED) {
        console.log('✅ Agent connected:', event.data);
        // Update UI to show agent is available
      }
      if (event.type === EventKeys.AGENT_DISCONNECTED) {
        console.log('❌ Agent disconnected:', event.data);
        // Update UI to show agent is unavailable
      }
    };

    // Register callback
    embedEvent.addCallback(handleAgentEvent);

    // Cleanup
    return () => {
      embedEvent.removeCallback(handleAgentEvent);
    };
  }, []);

  return <EmbedButton />;
}
```

#### Complete Agent Monitoring Example

```tsx theme={null}
import React, { useEffect, useState } from 'react';
import { embedEvent, EventKeys, EmbedButton } from '@revrag-ai/embed-react';

function VoiceAgentMonitor() {
  const [agentStatus, setAgentStatus] = useState<'idle' | 'connected' | 'disconnected'>('idle');
  const [agentIdentity, setAgentIdentity] = useState<string>('');
  const [connectionTime, setConnectionTime] = useState<Date | null>(null);

  useEffect(() => {
    const handleEvent = (event) => {
      // Handle agent connection
      if (event.type === EventKeys.AGENT_CONNECTED) {
        console.log('✅ Agent connected:', event);
        console.log('Identity:', event.data?.identity);
        console.log('Metadata:', event.data?.metadata);
        console.log('Timestamp:', event.timestamp);
        
        setAgentStatus('connected');
        setAgentIdentity(event.data?.identity || 'Unknown');
        setConnectionTime(new Date(event.timestamp));
        
        // Update UI - show green indicator, enable features, etc.
        // Example: Show notification, start analytics timer
      }

      // Handle agent disconnection
      if (event.type === EventKeys.AGENT_DISCONNECTED) {
        console.log('❌ Agent disconnected:', event);
        console.log('Identity:', event.data?.identity);
        console.log('Metadata:', event.data?.metadata);
        console.log('Timestamp:', event.timestamp);
        
        setAgentStatus('disconnected');
        
        // Calculate call duration if needed
        if (connectionTime) {
          const duration = Date.now() - connectionTime.getTime();
          console.log('Call duration:', duration / 1000, 'seconds');
        }
        
        // Update UI - show gray indicator, disable features, etc.
        // Example: Show feedback form, log analytics
      }
    };

    // Register event listener
    embedEvent.addCallback(handleEvent);

    // Cleanup listener on unmount
    return () => {
      embedEvent.removeCallback(handleEvent);
    };
  }, [connectionTime]);

  return (
    <div className="agent-monitor">
      <div className="status-indicator">
        <span className={`status-dot ${agentStatus}`} />
        <span>Agent Status: {agentStatus}</span>
      </div>
      
      {agentStatus === 'connected' && (
        <div className="agent-info">
          <p>✓ Voice agent is active</p>
          <p>Identity: {agentIdentity}</p>
          {connectionTime && (
            <p>Connected at: {connectionTime.toLocaleTimeString()}</p>
          )}
        </div>
      )}
      
      <EmbedButton />
    </div>
  );
}
```

#### Advanced: Multiple Event Listeners

```tsx theme={null}
import { useEffect } from 'react';
import { embedEvent, EventKeys } from '@revrag-ai/embed-react';

function MyApp() {
  useEffect(() => {
    // Analytics tracking
    const analyticsCallback = (event) => {
      if (event.type === EventKeys.AGENT_CONNECTED) {
        // Track to analytics service
        analytics.track('voice_agent_connected', {
          identity: event.data?.identity,
          timestamp: event.timestamp,
        });
      }
      if (event.type === EventKeys.AGENT_DISCONNECTED) {
        analytics.track('voice_agent_disconnected', {
          identity: event.data?.identity,
          timestamp: event.timestamp,
        });
      }
    };

    // UI updates
    const uiCallback = (event) => {
      if (event.type === EventKeys.AGENT_CONNECTED) {
        showNotification('Voice agent connected');
      }
      if (event.type === EventKeys.AGENT_DISCONNECTED) {
        showNotification('Voice agent disconnected');
      }
    };

    // Register multiple callbacks
    embedEvent.addCallback(analyticsCallback);
    embedEvent.addCallback(uiCallback);

    // Cleanup
    return () => {
      embedEvent.removeCallback(analyticsCallback);
      embedEvent.removeCallback(uiCallback);
    };
  }, []);

  return <YourApp />;
}
```

#### Use Cases for Agent Events

**AGENT\_CONNECTED:**

* Show visual indicators (green dot, badge)
* Enable voice-related features in UI
* Start analytics timers
* Update user presence status
* Show notifications to user
* Pause background music/media

**AGENT\_DISCONNECTED:**

* Update UI to show agent unavailable
* Log analytics (call duration, success)
* Show feedback forms
* Resume background media
* Clean up resources
* Save conversation state

#### What Gets Sent to Backend

When an agent event fires, the SDK automatically sends this data to your backend:

```typescript theme={null}
{
  event_id: "evt_1234567890_abc",        // Unique event ID
  type: "agent_start",                   // Event type (agent_start or agent_end)
  app_user_id: "user-123",               // Auto-added from USER_DATA
  session_id: "embed_session_...",       // Session identifier
  timestamp: "2024-01-15T10:30:00Z",     // ISO timestamp
  sdk: {
    sdk_name: "@revrag-ai/embed-react",
    sdk_version: "1.3.7",
    platform: "web"
  },
  data: {
    identity: "agent-001",               // Agent identity
    metadata: {                          // Additional metadata
      // ... agent-specific data
    }
  }
}
```

This allows you to:

* Track agent usage analytics
* Monitor call durations
* Understand user engagement patterns
* Build reports on voice agent interactions
* Audit agent connections

#### Event Data Structure

```typescript theme={null}
// Agent Connected Event
interface AgentConnectedEvent {
  type: 'agent_start';
  target: string;
  timestamp: number;              // Unix timestamp
  data?: {
    identity?: string;            // Agent identity
    metadata?: Record<string, unknown>;
  };
  userId?: string;
  sessionId: string;
  metadata?: Record<string, unknown>;
}

// Agent Disconnected Event
interface AgentDisconnectedEvent {
  type: 'agent_end';
  target: string;
  timestamp: number;              // Unix timestamp
  data?: {
    identity?: string;            // Agent identity
    metadata?: Record<string, unknown>;
  };
  userId?: string;
  sessionId: string;
  metadata?: Record<string, unknown>;
}
```

#### Handling Connection Errors

```tsx theme={null}
import { useEffect, useState } from 'react';
import { embed, EventKeys, EmbedButton } from '@revrag-ai/embed-react';

function AgentWithErrorHandling() {
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const handleEvent = (event) => {
      try {
        if (event.type === EventKeys.AGENT_CONNECTED) {
          setError(null);
          // Handle successful connection
        }
        if (event.type === EventKeys.AGENT_DISCONNECTED) {
          // Check if disconnection was due to error
          if (event.data?.metadata?.error) {
            setError('Agent connection lost: ' + event.data.metadata.error);
          }
        }
      } catch (err) {
        console.error('Error handling agent event:', err);
        setError('Failed to process agent event');
      }
    };

    embed.addCallback(handleEvent);
    return () => embed.removeCallback(handleEvent);
  }, []);

  return (
    <div>
      {error && (
        <div className="error-banner">
          {error}
        </div>
      )}
      <EmbedButton />
    </div>
  );
}
```

***

## 📱 Mobile Optimization

The widget automatically adjusts for mobile devices:

### Extra Small Screens (≤ 375px)

* iPhone SE, small Android devices
* Reduced padding and font sizes
* Optimized button and text layouts

### Small Screens (376px - 500px)

* Standard smartphones
* Balanced sizing for readability

***

## 🎯 Common Use Cases

### 1. Customer Support Widget with User Tracking

```tsx theme={null}
import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react';
import { useEffect } from 'react';

function App() {
  const { isInitialized } = useInitialize("your-api-key");
  const currentUser = useAuthUser(); // Your auth hook

  useEffect(() => {
    if (isInitialized && currentUser) {
      // Send user data when user logs in
      embedEvent.event({
        eventKey: EventKeys.USER_DATA,
        data: {
          app_user_id: currentUser.id,
          email: currentUser.email,
          name: currentUser.name,
          subscription_tier: currentUser.plan
        }
      });
    }
  }, [isInitialized, currentUser]);

  return (
    <>
      <YourMainContent />
      {isInitialized && (
        <EmbedButton 
          positioning="fixed"
          position={{ bottom: "24px", right: "24px" }}
        />
      )}
    </>
  );
}
```

### 2. E-commerce with Purchase Tracking

```tsx theme={null}
import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react';

function CheckoutPage() {
  const { isInitialized } = useInitialize("your-api-key");
  
  const handleCheckout = async (orderData) => {
    // ... process order
    
    // Track purchase event
    await embedEvent.event({
      eventKey: EventKeys.CUSTOM_EVENT,
      data: {
        event_name: 'purchase_completed',
        order_id: orderData.id,
        total: orderData.total,
        items: orderData.items.length
      }
    });
  };

  if (!isInitialized) return null;

  return (
    <div>
      <CheckoutForm onSubmit={handleCheckout} />
      <EmbedButton positioning="embedded" side="right" />
    </div>
  );
}
```

### 3. Help Section Widget with Agent Status

```tsx theme={null}
import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react';
import { useState, useEffect } from 'react';

function HelpPage() {
  const { isInitialized } = useInitialize("your-api-key");
  const [agentConnected, setAgentConnected] = useState(false);

  useEffect(() => {
    const handleAgentEvent = (event) => {
      if (event.type === EventKeys.AGENT_CONNECTED) {
        setAgentConnected(true);
      }
      if (event.type === EventKeys.AGENT_DISCONNECTED) {
        setAgentConnected(false);
      }
    };

    embedEvent.addCallback(handleAgentEvent);
    return () => embedEvent.removeCallback(handleAgentEvent);
  }, []);

  return (
    <div className="help-container">
      <h1>Help & Support</h1>
      {agentConnected && (
        <div className="status-badge">🟢 Agent Connected</div>
      )}
      <div style={{ position: 'relative', minHeight: '400px' }}>
        {isInitialized && (
          <EmbedButton 
            positioning="embedded"
            side="right"
          />
        )}
      </div>
    </div>
  );
}
```

### 4. With Bottom Navigation

```tsx theme={null}
function MobileApp() {
  const { isInitialized } = useInitialize("your-api-key");

  return (
    <>
      <YourContent />
      <BottomNavigation /> {/* height: 60px */}
      {isInitialized && (
        <EmbedButton 
          positioning="fixed"
          bottomOffset={60}  // Offset by navbar height
        />
      )}
    </>
  );
}
```

### 5. Multi-Department Support

```tsx theme={null}
function SupportPage() {
  const { isInitialized } = useInitialize("your-api-key");

  if (!isInitialized) return <div>Loading...</div>;

  return (
    <div className="support-grid">
      <div className="support-card">
        <h3>Sales Support</h3>
        <p>Questions about pricing and plans</p>
        <div style={{ position: 'relative', height: '400px' }}>
          <EmbedButton positioning="embedded" side="left" />
        </div>
      </div>

      <div className="support-card">
        <h3>Technical Support</h3>
        <p>Help with technical issues</p>
        <div style={{ position: 'relative', height: '400px' }}>
          <EmbedButton positioning="embedded" side="right" />
        </div>
      </div>
    </div>
  );
}
```

### 6. Contextual Events Based on User Actions

```tsx theme={null}
import { embedEvent, EventKeys, EmbedButton, useInitialize } from '@revrag-ai/embed-react';

function ProductPage({ product }) {
  const { isInitialized } = useInitialize("your-api-key");
  
  const handleAddToCart = async () => {
    // Send custom event when user adds to cart
    await embedEvent.event({
      eventKey: EventKeys.CUSTOM_EVENT,
      data: {
        event_name: 'product_added_to_cart',
        product_id: product.id,
        product_name: product.name,
        price: product.price
      }
    });
  };

  return (
    <div>
      <ProductDetails product={product} />
      <button onClick={handleAddToCart}>Add to Cart</button>
      {isInitialized && <EmbedButton />}
    </div>
  );
}
```

***

## 🔄 Complete Integration Example

Here's a complete example showing initialization, user tracking, event listening, and the widget all working together:

```tsx theme={null}
import React, { useEffect, useState } from 'react';
import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/style.css';

function App() {
  const { isInitialized, isLoading, error } = useInitialize("your-api-key");
  const [agentStatus, setAgentStatus] = useState('idle');
  const [userDataSent, setUserDataSent] = useState(false);

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

  // Listen to agent events
  useEffect(() => {
    const handleAgentEvent = (event) => {
      if (event.type === EventKeys.AGENT_CONNECTED) {
        console.log('Agent connected:', event);
        setAgentStatus('connected');
        // Show notification, enable features, etc.
      }
      if (event.type === EventKeys.AGENT_DISCONNECTED) {
        console.log('Agent disconnected:', event);
        setAgentStatus('disconnected');
        // Show feedback form, log analytics, etc.
      }
    };

    embedEvent.addCallback(handleAgentEvent);
    return () => embedEvent.removeCallback(handleAgentEvent);
  }, []);

  const initializeUserData = async () => {
    try {
      // Send user data first (required)
      await embedEvent.event({
        eventKey: EventKeys.USER_DATA,
        data: {
          app_user_id: 'user-123',
          email: 'user@example.com',
          name: 'John Doe',
          subscription: 'premium'
        }
      });
      setUserDataSent(true);
    } catch (error) {
      console.error('Failed to initialize user data:', error);
    }
  };

  // Send custom event on button click
  const handlePurchase = async () => {
    await embedEvent.event({
      eventKey: EventKeys.CUSTOM_EVENT,
      data: {
        event_name: 'purchase_completed',
        amount: 99.99,
        product_id: 'prod-123'
      }
    });
  };

  // Handle loading states
  if (isLoading) {
    return <div>Initializing AI Assistant...</div>;
  }

  if (error) {
    return <div>Error: {error}</div>;
  }

  if (!isInitialized || !userDataSent) {
    return <div>Loading...</div>;
  }

  return (
    <div className="app">
      <header>
        <h1>My Application</h1>
        <div className="status">
          Agent Status: <span className={agentStatus}>{agentStatus}</span>
        </div>
      </header>

      <main>
        <YourMainContent />
        <button onClick={handlePurchase}>Complete Purchase</button>
      </main>

      {/* AI Widget - will appear at bottom-right */}
      <EmbedButton />
    </div>
  );
}

export default App;
```

### Key Points in This Example:

1. **✅ CSS Import**: Imported at the top of the file
2. **✅ SDK Initialization**: Using `useInitialize` hook with loading/error states
3. **✅ User Data**: Sent first before rendering the widget
4. **✅ Event Listeners**: Set up to monitor agent connection status
5. **✅ Custom Events**: Tracked when user performs actions
6. **✅ Widget Rendering**: Only rendered after successful initialization

***

## ⚡ Framework-Specific Notes

### React + Vite

✅ Works perfectly with no additional configuration

### Next.js

✅ Fully compatible with both App Router and Pages Router
⚠️ Import CSS in root layout or \_app file

### Create React App

✅ Works out of the box
⚠️ Import CSS in index.tsx

### Tailwind CSS Projects

✅ No conflicts! The library uses custom `embed-*` prefixed classes
✅ Your Tailwind styles won't affect the widget
✅ Widget styles won't affect your app

***

## 🐛 Troubleshooting

### Widget appears unstyled

**Solution**: Make sure you've imported the CSS file:

```tsx theme={null}
import '@revrag-ai/embed-react/style.css';
```

### Widget not appearing

**Solution**: Ensure `useInitialize` has completed:

```tsx theme={null}
const { isInitialized } = useInitialize("your-api-key");
if (!isInitialized) return <div>Loading...</div>;
```

### Widget overlaps with bottom navigation

**Solution**: Use the `bottomOffset` prop:

```tsx theme={null}
<EmbedButton positioning="fixed" bottomOffset={60} />
```

### Widget too small on mobile

**Solution**: This is handled automatically! The widget is fully responsive.

### Events not being sent

**Solution**: Ensure you've sent USER\_DATA event first with `app_user_id`:

```tsx theme={null}
await embedEvent.event({
  eventKey: EventKeys.USER_DATA,
  data: {
    app_user_id: 'user-123',
    // ... other data
  }
});
```

### Agent event listeners not firing

**Solution**: Make sure callbacks are registered before agent connection:

```tsx theme={null}
useEffect(() => {
  const handleEvent = (event) => {
    // Your event handling logic
  };
  
  embedEvent.addCallback(handleEvent);
  return () => embedEvent.removeCallback(handleEvent);
}, []);
```

### "User identity not found" error

**Solution**: Send USER\_DATA event before any other events:

```tsx theme={null}
// ✅ Correct order
await embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: 'user-123' } });
await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { ... } });

// ❌ Wrong order
await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { ... } }); // Error!
```

### Custom events being blocked

**Solution**: Only `USER_DATA` and `CUSTOM_EVENT` are allowed for manual sending. Agent events (`AGENT_CONNECTED`, `AGENT_DISCONNECTED`) are auto-tracked and can only be listened to, not manually sent.

***

## 📋 Checklist

Before deploying, ensure:

**Basic Setup:**

* [ ] CSS file is imported
* [ ] API key is configured
* [ ] `isInitialized` is checked before rendering
* [ ] Parent container has `position: relative` (for embedded mode)
* [ ] Parent container has sufficient height (for embedded mode)
* [ ] Bottom offset is set if you have bottom navigation

**Event System:**

* [ ] USER\_DATA event sent first with `app_user_id`
* [ ] USER\_DATA sent before rendering EmbedButton
* [ ] Event listeners registered before agent connection
* [ ] Event listeners cleaned up on component unmount
* [ ] Custom events include proper context (screen, flow)

**Production Readiness:**

* [ ] Error handling for failed event sends
* [ ] Loading states during SDK initialization
* [ ] Agent connection status displayed to users
* [ ] Analytics tracking for agent events
* [ ] Proper cleanup of callbacks on unmount

***

## 🆘 Support

* 📧 Issues: [GitHub Issues](https://github.com/revrag-ai/embed-react/issues)
* 📖 Docs: [GitHub README](https://github.com/revrag-ai/embed-react)
* 💬 Discussions: [GitHub Discussions](https://github.com/revrag-ai/embed-react/discussions)

***

## 🎉 You're All Set!

The widget is now ready to use. It's:

* ✅ Fully responsive
* ✅ Framework-agnostic
* ✅ Tailwind-compatible
* ✅ Production-ready
* ✅ Mobile-optimized
* ✅ Real-time event tracking
* ✅ Voice agent monitoring
* ✅ User context aware

### Quick Reference

**Import everything you need:**

```tsx theme={null}
import { 
  EmbedButton,       // The main widget component
  EmbedProvider,     // Built-in provider for automatic widget management
  useInitialize,     // SDK initialization hook
  embedEvent,        // Event management API
  EventKeys,         // Event type constants

} from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/style.css';
```

**Initialize and track:**

```tsx theme={null}
// 1. Initialize SDK
const { isInitialized } = useInitialize("your-api-key");

// 2. Send user data
await embedEvent.event({
  eventKey: EventKeys.USER_DATA,
  data: { app_user_id: 'user-123' }
});

// 3. Listen to agent events
embedEvent.addCallback((event) => {
  if (event.type === EventKeys.AGENT_CONNECTED) {
    console.log('Agent connected!');
  }
});

// 4. Render widget
<EmbedButton />
```

Happy coding! 🚀
