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
- useSyncContext - Hook to automatically sync context with the AI
- VowelAgent - Pre-built voice agent UI component
- VowelMicrophone - Standalone microphone button component
Installation
npm install @vowel.to/clientBasic Setup
1. Create Vowel Client
First, create and configure your Vowel client:
// 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
// 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:
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.
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.
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 classesstyle- Inline styles
VowelMicrophone
Standalone microphone button component.
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 classesstyle- Inline styles
Hooks
useVowel
Access the Vowel client and state:
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:
{
client: Vowel;
state: VoiceSessionState;
}useSyncContext
Automatically sync context with the Vowel client. This hook updates the AI's context whenever the provided value changes, making it easy to keep the AI aware of dynamic application state.
import { useSyncContext } from '@vowel.to/client/react';
function ProductPage({ productId }: { productId: string }) {
const product = useProduct(productId);
// Automatically sync product context to Vowel
useSyncContext({
page: 'product',
productId: product.id,
productName: product.name,
price: product.price,
inStock: product.inStock
});
return <div>{product.name}</div>;
}Parameters:
context- Context object to sync (Record<string, unknown> | null). Usenullto clear context.
Features:
- Automatically updates context when the value changes
- Clears context on component unmount
- Uses deep comparison to avoid unnecessary updates
- Works seamlessly with React state and props
Example: Conditional Context
function UserProfile({ userId }: { userId: string | null }) {
const user = useUser(userId);
// Sync user context, or clear if no user
useSyncContext(
user ? {
userId: user.id,
userName: user.name,
userRole: user.role
} : null
);
return user ? <div>{user.name}</div> : <div>No user</div>;
}Example: Page Context
function CheckoutPage() {
const cart = useCart();
// Sync cart context
useSyncContext({
page: 'checkout',
cartTotal: cart.total,
itemCount: cart.items.length,
shippingAddress: cart.shippingAddress
});
return <div>Checkout</div>;
}See the Dynamic Context recipe for more examples and patterns.
Common Patterns
Auto-Start Session
function App() {
const { client } = useVowel();
useEffect(() => {
client.startSession();
return () => {
client.stopSession();
};
}, [client]);
return <YourApp />;
}Conditional Rendering Based on State
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
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
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
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
// 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:
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
<VowelAgent
position="bottom-right"
style={{
'--vowel-primary-color': '#007bff',
'--vowel-background': '#ffffff',
'--vowel-border-radius': '12px'
} as React.CSSProperties}
/>CSS Classes
<VowelAgent
position="bottom-right"
className="my-custom-agent"
/>.my-custom-agent {
/* Your custom styles */
}Related
- React Router - React Router integration
- Next.js - Next.js integration
- Adapters - Navigation and automation adapters
- API Reference - React API documentation