React Component
Embed the ThunderPhone voice widget in a React application
The ThunderPhoneWidget component renders a glassmorphic call bar with built-in controls for muting, ending the call, and displaying connection status. It is the fastest way to add voice AI to a React app.
Installation
npm install @thunderphone/widgetBasic Usage
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function App() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
/>
)
}Props
The component accepts the following props via ThunderPhoneWidgetProps:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
publishableKey | string | Yes | -- | Publishable API key (pk_live_...) from the Developers settings. The agent is resolved automatically from the key's widget configuration. |
theme | 'light' | 'dark' | No | 'light' | Color scheme. Applies tp--light or tp--dark class to the widget root. |
primaryColor | string | No | '#000000' (light) / '#ffffff' (dark) | CSS color string used as the accent color (call button, waveform, active indicators). |
title | string | No | 'Voice assistant' | Text displayed in the widget bar. |
position | 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | No | 'bottom-right' | Fixed viewport position for the widget. |
apiBase | string | No | 'https://api.thunderphone.com/v1' | API base URL override. |
language | string | No | -- | Per-session language override -- a language code or locale such as en, es, or fr-FR. When unset, the agent's configured language applies. |
voice | string | No | -- | Per-session voice override -- a voice name such as maria. When unset, the agent's configured voice applies. |
context | string | No | -- | Per-session factual page or site context passed to the agent (for example, details of the page the visitor is viewing). Truncated server-side to 12,000 characters. |
onConnect | () => void | No | -- | Called when the voice session successfully connects. |
onDisconnect | () => void | No | -- | Called when the session ends. |
onError | (error) => void | No | -- | Called on errors. The error object has error (code) and message fields. |
className | string | No | -- | Additional CSS class name applied to the widget container. |
ringtone | boolean | string | No | false | Play a ringtone while connecting. true for the default ringtone, or a URL string for custom audio. |
Examples
Dark Theme with Custom Color
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function App() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
theme="dark"
primaryColor="#8b5cf6"
title="Talk to our AI"
/>
)
}Custom Position
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function App() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
position="bottom-left"
/>
)
}Per-Session Language, Voice, and Context
The language, voice, and context props are forwarded to the session request (POST /widget/session) when a call starts, overriding the agent's configured defaults for that session:
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function PricingPageWidget() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
language="es"
voice="maria"
context="Page: Pricing. Plans: Starter $29/mo, Pro $99/mo. Annual billing saves 20%."
/>
)
}Use context to give the agent factual knowledge about the page the visitor is on -- product details, pricing, or page-specific FAQs. It is truncated server-side to 12,000 characters.
With Event Callbacks
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function SupportWidget() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
onConnect={() => {
console.log('Voice session connected')
analytics.track('widget_call_started')
}}
onDisconnect={() => {
console.log('Voice session ended')
analytics.track('widget_call_ended')
}}
onError={(error) => {
console.error(`Widget error: ${error.error} - ${error.message}`)
}}
/>
)
}With Custom Styling
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function BrandedWidget() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
primaryColor="#4a90d9"
className="my-custom-widget"
/>
)
}.my-custom-widget .tp-button--end {
background-color: #e74c3c;
}See the Styling guide for all available CSS classes and custom properties.
With Ringtone
Play a phone-ringing sound while the connection is being established:
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function PhoneWidget() {
return (
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
ringtone={true}
/>
)
}Use a custom ringtone by passing an audio file URL:
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
ringtone="https://example.com/my-ringtone.mp3"
/>The ringtone loops while the widget is in the connecting state and fades out smoothly when the agent connects.
With Custom API Base
<ThunderPhoneWidget
publishableKey="pk_live_your_publishable_key"
apiBase="https://your-proxy.example.com/v1"
/>Error Handling
When the onError callback fires, it receives an error object with two fields:
| Field | Type | Description |
|---|---|---|
error | string | Machine-readable error code |
message | string | Human-readable error description |
Common error codes include domain not allowed, agent not found, and invalid API key.