This document contains internal technical implementation details for the VAPI Widget. For user documentation and API reference, see README.md.
Note: For user-facing features list, see README.md.
This document focuses on the technical implementation details of the widget's features.
Located at src/components/VapiWidget.tsx, this component handles:
- Multi-mode Support: Voice-only, chat-only, or hybrid interactions
- State Management: Call state, conversation history, UI state
- Event Handling: VAPI events and user interactions
- UI Rendering: Floating button and expanded interface
- useVapiWidget: Main hook that combines voice and chat functionality
- useVapiCall: Handles voice call integration with VAPI
- useVapiChat: Manages chat functionality with VAPI or custom API
Note: Hybrid mode is not fully supported yet. When switching between modes, conversation history is not maintained.
interface VapiWidgetProps {
// Required
publicKey: string; // VAPI public key
// VAPI Configuration (provide at least one)
assistantId?: string; // VAPI assistant ID (supported by both voice and chat)
assistant?: any; // Full assistant object (voice only)
assistantOverrides?: any; // Assistant overrides (supported by both voice and chat)
// API Configuration
apiUrl?: string; // Optional custom API URL for chat mode
// Layout & Position
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
size?: 'tiny' | 'compact' | 'full';
radius?: 'none' | 'small' | 'medium' | 'large';
// Mode & Theme
mode?: 'voice' | 'chat' | 'hybrid'; // Default: 'voice'
theme?: 'light' | 'dark'; // Default: 'light'
// Colors
baseColor?: string; // Background color
accentColor?: string; // Primary accent color
buttonBaseColor?: string; // Floating button background
buttonAccentColor?: string; // Floating button text/icon color
// Text & Labels
mainLabel?: string; // Widget header text (default: 'Talk with AI')
startButtonText?: string; // Voice start button (default: 'Start')
endButtonText?: string; // Voice end button (default: 'End Call')
// Empty State Messages
emptyVoiceMessage?: string;
emptyVoiceActiveMessage?: string;
emptyChatMessage?: string;
emptyHybridMessage?: string;
// Legal & Consent
requireConsent?: boolean; // Show consent form on first use
termsContent?: string; // Custom consent text
localStorageKey?: string; // Key for storing consent
// Display Options
showTranscript?: boolean; // Show/hide transcript in voice mode
// Event Handlers
onCallStart?: () => void;
onCallEnd?: () => void;
onMessage?: (message: any) => void;
onError?: (error: Error) => void;
}The widget manages several state variables:
isExpanded: Controls widget expansionhasConsent: Tracks user consent statuschatInput: Current chat input value- Voice state via
useVapiCallhook - Chat state via
useVapiChathook - Combined conversation history
The widget supports three types of VAPI configuration:
// Simple assistant ID (supports both voice and chat)
assistantId: "assistant-id"
// Assistant with overrides (supports both voice and chat)
assistantId: "assistant-id"
assistantOverrides: {
variableValues: { name: 'John' },
}
// Full assistant object (voice only)
assistant: {
model: {
provider: 'openai',
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' }
]
},
voice: {
provider: '11labs',
voiceId: 'burt'
}
// ... full assistant configuration
}import { VapiWidget } from 'vapi-client-widget-web';
function App() {
return (
<VapiWidget
publicKey="your-vapi-public-key"
assistantId="your-assistant-id"
position="bottom-right"
theme="light"
accentColor="#3B82F6"
size="compact"
onCallStart={() => console.log('Call started')}
onCallEnd={() => console.log('Call ended')}
/>
);
}<VapiWidget
publicKey="your-vapi-public-key"
assistantId="your-assistant-id"
mode="chat"
theme="dark"
size="full"
onMessage={(msg) => console.log('Message:', msg)}
/><VapiWidget
publicKey="your-vapi-public-key"
assistantId="your-assistant-id"
assistantOverrides={{
variableValues: { name: 'John' },
}}
mode="hybrid"
showTranscript={true}
requireConsent={true}
termsContent="Custom terms and conditions..."
/><VapiWidget
publicKey="your-vapi-public-key"
assistant={{
model: {
provider: 'openai',
model: 'gpt-4o-mini',
messages: [{ role: 'system', content: 'You are a helpful assistant.' }],
},
voice: {
provider: '11labs',
voiceId: 'burt',
},
}}
mode="voice"
size="full"
/><!DOCTYPE html>
<html>
<head>
<script src="./dist/widget.umd.js"></script>
</head>
<body>
<div
data-client-widget="VapiWidget"
data-props='{
"publicKey": "your-vapi-public-key",
"assistantId": "your-assistant-id",
"mode": "hybrid",
"position": "bottom-right",
"theme": "dark",
"accentColor": "#10b981",
"size": "compact"
}'
></div>
</body>
</html>const widget = new WidgetLoader({
container: '#widget-container',
component: 'VapiWidget',
props: {
publicKey: 'your-vapi-public-key',
assistantId: 'your-assistant-id',
assistantOverrides: {
model: 'gpt-4',
},
mode: 'chat',
theme: 'light',
baseColor: '#f3f4f6',
accentColor: '#8b5cf6',
size: 'full',
},
});- Tiny Mode (Voice Only):
- Small circular button (48×48px)
- Expands to 80×80px with red glow when active
- Direct voice toggle without expanded view
- Compact Mode:
- Pill-shaped button with icon and label
- Shows connection status via animated icon
- Full Mode:
- Larger pill button with prominent label
- Enhanced hover effects
- Header:
- Assistant name/label
- Dynamic status message
- Reset button (clears conversation)
- Close button
- Conversation Area:
- Message bubbles with role distinction
- Markdown rendering support
- Auto-scrolling to latest message
- Typing indicators
- Controls Section:
- Voice mode: Start/End call button
- Chat mode: Text input with send button
- Hybrid mode: Both input field and voice button
- Click microphone to start/end calls
- Optional transcript display
- Volume level visualization
- Voice activity indicators
- Text input always visible
- Enter key to send messages
- Markdown formatting support
- Typing indicators
- Seamless switching between voice and chat
- Conversation cleared when switching modes
- Input disabled during voice calls
- Smart mode detection
Enable user consent flow:
<VapiWidget
requireConsent={true}
termsContent="By using this service, you agree to..."
localStorageKey="my_app_vapi_consent"
/>Provide helpful context-specific messages:
<VapiWidget
emptyVoiceMessage="Click the mic to start your conversation"
emptyVoiceActiveMessage="I'm listening..."
emptyChatMessage="Type your question below"
emptyHybridMessage="Choose voice or text to begin"
/>Hide transcript for voice-only experiences:
<VapiWidget
mode="voice"
showTranscript={false} // Shows only volume indicator
/>- Create VAPI account at vapi.ai
- Create an assistant or use inline configuration
- Get your public API key
- Configure assistant settings
@vapi-ai/web: Core VAPI SDKreact: React frameworkreact-dom: React DOM rendererreact-markdown: Markdown rendering@phosphor-icons/react: Icon library
- Modern browser with WebRTC support (for voice)
- Microphone permissions (for voice)
- Secure context (HTTPS) for production
The widget handles common error scenarios:
- Microphone access denied
- Clear error messaging
- Fallback to chat mode (in hybrid)
- Network connectivity issues
- VAPI service unavailability
- Automatic retry mechanisms
- Invalid API keys
- Missing configuration
- Helpful error messages
- Lazy loading of VAPI SDK
- Efficient re-rendering with React hooks
- Debounced typing indicators
- Smooth animations with CSS
- Proper cleanup of event listeners
- Connection termination on unmount
- Conversation history limits
# Install dependencies
npm install
# Build all packages
npm run build:all
# Run example app
cd example && npm run devTest different configurations:
// Test voice mode
<VapiWidget publicKey="..." assistantId="..." mode="voice" />
// Test chat mode
<VapiWidget publicKey="..." assistantId="..." mode="chat" />
// Test hybrid mode
<VapiWidget publicKey="..." assistantId="..." mode="hybrid" />- Verify script inclusion
- Check console for errors
- Ensure container element exists
- Check microphone permissions
- Verify VAPI credentials
- Ensure HTTPS context
- Try chat mode as fallback
- Verify VAPI configuration
- Check network connectivity
- Inspect console for API errors
- Ensure Tailwind CSS is loaded
- Check for CSS conflicts
- Verify theme settings
Enable detailed logging:
// Check console for detailed logs
window.DEBUG_VAPI = true;