Skip to content

React Integration

Learn how to integrate Vowel into your React applications using the provided React components and hooks.

Overview

Vowel provides React-specific exports for seamless integration:

  • VowelProvider - Context provider for the Vowel client
  • useVowel - Hook to access the Vowel client
  • VowelAgent - Pre-built voice agent UI component
  • VowelMicrophone - Standalone microphone button component

Installation

bash
npm install @vowel.to/client

Basic Setup

1. Create Vowel Client

First, create and configure your Vowel client:

typescript
// vowel.client.ts
import { Vowel, createDirectAdapters } from '@vowel.to/client';

export function createVowelClient(router: any) {
  const { navigationAdapter, automationAdapter } = createDirectAdapters({
    navigate: (path) => router.push(path),
    routes: [
      { path: '/', description: 'Home page' },
      { path: '/products', description: 'Product catalog' },
      { path: '/cart', description: 'Shopping cart' }
    ],
    enableAutomation: true
  });

  const vowel = new Vowel({
    appId: 'your-app-id',
    navigationAdapter,
    automationAdapter,
    voiceConfig: {
      name: 'Puck',
      language: 'en-US'
    }
  });

  // Register custom actions
  vowel.registerAction('searchProducts', {
    description: 'Search for products',
    parameters: {
      query: { type: 'string', description: 'Search query' }
    }
  }, async ({ query }) => {
    // Your search logic
    return { success: true };
  });

  return vowel;
}

2. Wrap App with VowelProvider

tsx
// App.tsx
import { VowelProvider, VowelAgent } from '@vowel.to/client/react';
import { createVowelClient } from './vowel.client';
import { useRouter } from 'next/navigation';

function App() {
  const router = useRouter();
  const vowel = createVowelClient(router);

  return (
    <VowelProvider client={vowel}>
      <YourApp />
      <VowelAgent position="bottom-right" />
    </VowelProvider>
  );
}

3. Use the Hook

Access the Vowel client anywhere in your component tree:

tsx
import { useVowel } from '@vowel.to/client/react';

function MyComponent() {
  const { client, state } = useVowel();

  const handleNotify = async () => {
    await client.notifyEvent('Hello from React!');
  };

  return (
    <div>
      <p>Connected: {state.isConnected ? 'Yes' : 'No'}</p>
      <p>AI Speaking: {state.isAISpeaking ? 'Yes' : 'No'}</p>
      <button onClick={handleNotify}>Notify</button>
    </div>
  );
}

Components

VowelProvider

Context provider that makes the Vowel client available to all child components.

tsx
import { VowelProvider } from '@vowel.to/client/react';

<VowelProvider client={vowel}>
  {children}
</VowelProvider>

Props:

  • client - The Vowel client instance (required)
  • children - React children

VowelAgent

Pre-built voice agent UI with microphone button and visual feedback.

tsx
import { VowelAgent } from '@vowel.to/client/react';

<VowelAgent 
  position="bottom-right"
  showTranscript={true}
  autoStart={false}
/>

Props:

  • position - 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' (default: 'bottom-right')
  • showTranscript - Show live transcript (default: true)
  • autoStart - Auto-start session on mount (default: false)
  • className - Additional CSS classes
  • style - Inline styles

VowelMicrophone

Standalone microphone button component.

tsx
import { VowelMicrophone } from '@vowel.to/client/react';

<VowelMicrophone 
  size="medium"
  variant="floating"
/>

Props:

  • size - 'small' | 'medium' | 'large' (default: 'medium')
  • variant - 'floating' | 'inline' (default: 'floating')
  • className - Additional CSS classes
  • style - Inline styles

Hooks

useVowel

Access the Vowel client and state:

tsx
import { useVowel } from '@vowel.to/client/react';

function MyComponent() {
  const { client, state } = useVowel();

  // Access client methods
  const startSession = () => client.startSession();
  const stopSession = () => client.stopSession();
  const notify = (msg: string) => client.notifyEvent(msg);

  // Access state
  console.log(state.isConnected);
  console.log(state.isUserSpeaking);
  console.log(state.isAISpeaking);
  console.log(state.isAIThinking);

  return (
    <div>
      {state.isConnected ? 'Connected' : 'Disconnected'}
    </div>
  );
}

Returns:

typescript
{
  client: Vowel;
  state: VoiceSessionState;
}

Common Patterns

Auto-Start Session

tsx
function App() {
  const { client } = useVowel();

  useEffect(() => {
    client.startSession();
    
    return () => {
      client.stopSession();
    };
  }, [client]);

  return <YourApp />;
}

Conditional Rendering Based on State

tsx
function VoiceStatus() {
  const { state } = useVowel();

  return (
    <div>
      {state.isConnected && (
        <div className="status-indicator">
          {state.isUserSpeaking && <span>🎤 Listening...</span>}
          {state.isAIThinking && <span>🤔 Thinking...</span>}
          {state.isAISpeaking && <span>🔊 Speaking...</span>}
        </div>
      )}
    </div>
  );
}

Event Notification on Action

tsx
function AddToCartButton({ productId }: { productId: string }) {
  const { client } = useVowel();

  const handleAddToCart = async () => {
    await addToCart(productId);
    
    // Notify via voice
    await client.notifyEvent('Item added to cart');
  };

  return (
    <button onClick={handleAddToCart}>
      Add to Cart
    </button>
  );
}

Custom Voice Action Hook

tsx
function useVoiceAction<T>(
  name: string,
  definition: VowelAction,
  handler: ActionHandler<T>
) {
  const { client } = useVowel();

  useEffect(() => {
    client.registerAction(name, definition, handler);
  }, [client, name, definition, handler]);
}

// Usage
function ProductSearch() {
  useVoiceAction('searchProducts', {
    description: 'Search for products',
    parameters: {
      query: { type: 'string', description: 'Search query' }
    }
  }, async ({ query }) => {
    const results = await searchProducts(query);
    return { success: true, data: results };
  });

  return <SearchResults />;
}

Voice Notification Hook

tsx
function useVoiceNotification() {
  const { client } = useVowel();

  const notify = useCallback(async (message: string, context?: any) => {
    if (client.state.isConnected) {
      await client.notifyEvent(message, context);
    }
  }, [client]);

  return { notify };
}

// Usage
function OrderConfirmation({ order }: { order: Order }) {
  const { notify } = useVoiceNotification();

  useEffect(() => {
    notify('Order confirmed!', { orderId: order.id });
  }, [order, notify]);

  return <div>Order #{order.id} confirmed!</div>;
}

Complete Example

tsx
// App.tsx
import { VowelProvider, VowelAgent, useVowel } from '@vowel.to/client/react';
import { Vowel, createDirectAdapters } from '@vowel.to/client';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

// Create client
function createVowelClient(router: any) {
  const { navigationAdapter, automationAdapter } = createDirectAdapters({
    navigate: (path) => router.push(path),
    routes: [
      { path: '/', description: 'Home' },
      { path: '/products', description: 'Products' },
      { path: '/cart', description: 'Cart' }
    ],
    enableAutomation: true
  });

  const vowel = new Vowel({
    appId: 'your-app-id',
    navigationAdapter,
    automationAdapter
  });

  // Register actions
  vowel.registerAction('addToCart', {
    description: 'Add product to cart',
    parameters: {
      productId: { type: 'string', required: true }
    }
  }, async ({ productId }) => {
    await addToCart(productId);
    return { success: true };
  });

  return vowel;
}

// Main app component
function AppContent() {
  const { client, state } = useVowel();

  useEffect(() => {
    // Auto-start session
    client.startSession();

    return () => {
      client.stopSession();
    };
  }, [client]);

  return (
    <div>
      <header>
        <h1>My Store</h1>
        {state.isConnected && (
          <div className="voice-status">
            Voice: {state.isAISpeaking ? 'Speaking' : 'Ready'}
          </div>
        )}
      </header>
      
      <main>
        <YourContent />
      </main>

      {/* Voice agent UI */}
      <VowelAgent position="bottom-right" />
    </div>
  );
}

// Root component
export default function App() {
  const router = useRouter();
  const vowel = createVowelClient(router);

  return (
    <VowelProvider client={vowel}>
      <AppContent />
    </VowelProvider>
  );
}

TypeScript Support

Full TypeScript support with type definitions:

tsx
import type { 
  VowelProviderProps,
  VowelAgentProps,
  VowelMicrophoneProps,
  VowelContextType
} from '@vowel.to/client/react';

// Typed component
const MyComponent: React.FC = () => {
  const { client, state }: VowelContextType = useVowel();
  
  return <div>{state.isConnected && 'Connected'}</div>;
};

Styling

Custom Styles

tsx
<VowelAgent 
  position="bottom-right"
  style={{
    '--vowel-primary-color': '#007bff',
    '--vowel-background': '#ffffff',
    '--vowel-border-radius': '12px'
  } as React.CSSProperties}
/>

CSS Classes

tsx
<VowelAgent 
  position="bottom-right"
  className="my-custom-agent"
/>
css
.my-custom-agent {
  /* Your custom styles */
}