New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

@thunderphone/widget

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thunderphone/widget

Embeddable voice widget for ThunderPhone AI agents

latest
Source
npmnpm
Version
1.3.0
Version published
Weekly downloads
101
8.6%
Maintainers
1
Weekly downloads
 
Created
Source

ThunderPhone Widget

Embed a ThunderPhone voice AI agent on any website. Users can talk to your agent directly from your site using their browser microphone.

Installation

npm install @thunderphone/widget

Quick Start

import { ThunderPhoneWidget } from '@thunderphone/widget'
import '@thunderphone/widget/style.css'

function App() {
  return (
    <ThunderPhoneWidget
      publishableKey="pk_live_your_publishable_key"
    />
  )
}

Setup

  • Log in to app.thunderphone.com
  • Go to Developers to create a publishable API key and configure allowed domains
  • Create a Web Widget and select the agent you want to expose
  • Use your publishable key in the widget code above

Props

PropTypeRequiredDescription
publishableKeystringYesYour publishable API key (pk_live_...). The agent is resolved from the key's configuration.
apiBasestringNoAPI base URL (defaults to https://api.thunderphone.com/v1)
onConnect() => voidNoCalled when the voice session connects
onDisconnect() => voidNoCalled when the session ends
onError(error) => voidNoCalled on errors. Error has error (code) and message fields
classNamestringNoAdditional CSS class for the widget container
ringtoneboolean | stringNoPlay a ringtone while connecting. true for default, or a URL for custom audio. Disabled by default

Headless Hook

Use the useThunderPhone hook to build a completely custom UI while the widget handles the voice connection.

import { useThunderPhone } from '@thunderphone/widget'

function CustomCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  const handleClick = () => {
    if (phone.state === 'connected') {
      phone.disconnect()
    } else {
      phone.connect()
    }
  }

  return (
    <>
      <button onClick={handleClick} disabled={phone.state === 'connecting'}>
        {phone.state === 'connecting'
          ? 'Connecting...'
          : phone.state === 'connected'
            ? 'End call'
            : 'Start call'}
      </button>
      {phone.audio}
    </>
  )
}

Important: Always render phone.audio somewhere in your component tree. It's an invisible element that handles the audio connection.

Hook Options

OptionTypeRequiredDescription
publishableKeystringYesYour publishable API key
apiBasestringNoAPI base URL override
onConnect() => voidNoCalled when the voice session connects
onDisconnect() => voidNoCalled when the session ends
onError(error) => voidNoCalled on errors
ringtoneboolean | stringNoPlay a ringtone while connecting. true for default, or a URL for custom audio

Hook Return Value

PropertyTypeDescription
state'idle' | 'connecting' | 'connected' | 'disconnected' | 'error'Current connection state
connect() => voidStart a voice session
disconnect() => voidEnd the current session
toggleMute() => voidToggle microphone mute
isMutedbooleanWhether the mic is muted
errorstring | undefinedError message if state is 'error'
agentNamestring | undefinedName of the connected agent
audioReactNodeInvisible element — must be rendered in the tree

Ringtone

Play a ringing sound while the widget connects, to simulate a phone call:

<ThunderPhoneWidget
  publishableKey="pk_live_your_publishable_key"
  ringtone={true}
/>

Use a custom audio file by passing a URL:

<ThunderPhoneWidget
  publishableKey="pk_live_your_publishable_key"
  ringtone="https://example.com/my-ringtone.mp3"
/>

The ringtone loops during the connecting state and fades out when the agent connects. It is opt-in and disabled by default.

The headless hook accepts the same option:

const phone = useThunderPhone({ publishableKey, ringtone: true })

And the script-tag mount API:

ThunderPhone.mount({ element: '#thunderphone', publishableKey: '...', ringtone: true })

Styling

The widget uses plain CSS with tp- prefixed classes, so it won't conflict with your styles. You can override any of these classes:

ClassElement
.tp-widgetOuter container (inline-flex)
.tp-buttonAll buttons (circular, 44px)
.tp-button--startStart/mic button
.tp-button--muteMute toggle button
.tp-button--endEnd call button
.tp-statusStatus text container
.tp-status__nameAgent name
.tp-status__textConnection state / timer

Example — custom colors:

.tp-button--start {
  background: #4f46e5;
}
.tp-button--start:hover {
  background: #4338ca;
}

Domain Restrictions

Your publishable key can be restricted to specific domains in the Developers settings page. Requests from unlisted domains will be rejected. localhost is always allowed for development.

Wildcard subdomains are supported: *.example.com matches app.example.com, docs.example.com, etc.

CDN / Script Tag

If you're not using a bundler, you can load the widget via script tag. This version bundles React internally so no dependencies are needed.

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/v0.4.0/style.css" />
<script src="https://cdn.thunderphone.com/widget/v0.4.0/widget.js"></script>

<div id="thunderphone"></div>

<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>

Use latest instead of a version number for the most recent release (cached for 5 minutes):

https://cdn.thunderphone.com/widget/latest/widget.js
https://cdn.thunderphone.com/widget/latest/style.css

ThunderPhone.mount() accepts the same options as the React component props above, plus element (CSS selector or DOM element). It returns a handle for cleanup:

const widget = ThunderPhone.mount({ element: '#thunderphone', publishableKey: '...' })

// Later, to remove the widget:
widget.unmount()

License

MIT

FAQs

Package last updated on 18 Aug 2026

Related posts