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/widget
Basic Usage
import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'
function App () {
return (
< ThunderPhoneWidget
publishableKey = "pk_live_your_publishable_key"
/>
)
}
You must import the CSS file for the widget to render correctly. Without it, the widget will be unstyled.
Props
The component accepts the following props via ThunderPhoneWidgetProps:
Prop Type Required Default Description publishableKeystringYes — 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. primaryColorstringNo '#6366f1'CSS color string used as the accent color (call button, active indicators). titlestringNo '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. apiBasestringNo 'https://api.thunderphone.com/v1'API base URL override. onConnect() => voidNo — Called when the voice session successfully connects. onDisconnect() => voidNo — Called when the session ends. onError(error) => voidNo — Called on errors. The error object has error (code) and message fields. classNamestringNo — Additional CSS class name applied to the widget container. ringtoneboolean | stringNo falsePlay 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"
/>
)
}
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
You only need to set apiBase if you are using a self-hosted or proxy API endpoint. The default points to https://api.thunderphone.com/v1.
< 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 errorstringMachine-readable error code messagestringHuman-readable error description
Common error codes include domain not allowed, agent not found, and invalid API key.
Next Steps
Headless Hook Need full control over the UI? Use the useThunderPhone hook instead.
Styling Customize colors, sizes, and layout with CSS custom properties.