---
title: "Komponen React"
description: "Sematkan widget suara ThunderPhone dalam aplikasi React"
---

Komponen `ThunderPhoneWidget` merender bilah panggilan bergaya glassmorphism dengan kontrol bawaan untuk membisukan, mengakhiri panggilan, dan menampilkan status koneksi. Ini adalah cara tercepat untuk menambahkan agen suara AI ke aplikasi React.

## Instalasi

```bash
npm install @thunderphone/widget
```

## Penggunaan Dasar

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

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

<Warning>
  Anda **harus** mengimpor file CSS agar widget dirender dengan benar. Tanpanya, widget tidak akan memiliki gaya.
</Warning>

---

## Properti

Komponen menerima properti berikut melalui `ThunderPhoneWidgetProps`:

| Properti | Tipe | Wajib | Default | Deskripsi |
|------|------|----------|---------|-------------|
| `publishableKey` | `string` | Ya | -- | Kunci API yang dapat dipublikasikan (`pk_live_...`) dari **Widget Web** di dashboard. Agen ditentukan secara otomatis dari konfigurasi widget kunci tersebut. |
| `theme` | `'light' \| 'dark'` | Tidak | `'light'` | Skema warna. Menerapkan class `tp--light` atau `tp--dark` pada root widget. |
| `primaryColor` | `string` | Tidak | `'#000000'` (terang) / `'#ffffff'` (gelap) | String warna CSS yang digunakan sebagai warna aksen (tombol panggilan, bentuk gelombang, indikator aktif). |
| `title` | `string` | Tidak | `'Voice assistant'` | Teks yang ditampilkan di bilah widget. |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | Tidak | `'bottom-right'` | Posisi viewport tetap untuk widget. |
| `apiBase` | `string` | Tidak | `'https://api.thunderphone.com/v1'` | Penimpaan URL dasar API. |
| `language` | `string` | Tidak | -- | Penimpaan bahasa per sesi -- kode bahasa atau lokal seperti `en`, `es`, atau `fr-FR`. Jika tidak ditetapkan, bahasa yang dikonfigurasi untuk agen akan digunakan. Memerlukan `language` di [browser overrides](/id/guides/embed-web-widget) kunci tersebut. |
| `voice` | `string` | Tidak | -- | Penimpaan suara per sesi -- nama suara seperti `maria`. Jika tidak ditetapkan, suara yang dikonfigurasi untuk agen akan digunakan. Memerlukan `voice` di [browser overrides](/id/guides/embed-web-widget) kunci tersebut. |
| `context` | `string` | Tidak | -- | Konteks faktual halaman atau situs per sesi yang diteruskan ke agen (misalnya, detail halaman yang sedang dilihat pengunjung). Dipotong di sisi server hingga 12.000 karakter. Memerlukan `context` di [browser overrides](/id/guides/embed-web-widget) kunci tersebut. |
| `onConnect` | `() => void` | Tidak | -- | Dipanggil saat sesi suara berhasil terhubung. |
| `onDisconnect` | `() => void` | Tidak | -- | Dipanggil saat sesi berakhir. |
| `onError` | `(error) => void` | Tidak | -- | Dipanggil saat terjadi kesalahan. Objek `error` memiliki field `error` (kode) dan `message`. |
| `className` | `string` | Tidak | -- | Nama class CSS tambahan yang diterapkan pada container widget. |
| `ringtone` | `boolean \| string` | Tidak | `false` | Putar nada dering saat menghubungkan. `true` untuk nada dering default, atau string URL untuk audio kustom. |

---

## Contoh

### Tema Gelap dengan Warna Kustom

```tsx
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"
    />
  )
}
```

### Posisi Kustom

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

function App() {
  return (
    <ThunderPhoneWidget
      publishableKey="pk_live_your_publishable_key"
      position="bottom-left"
    />
  )
}
```

### Bahasa, Suara, dan Konteks per Sesi

Prop `language`, `voice`, dan `context` diteruskan ke permintaan sesi (`POST /widget/session`) saat panggilan dimulai, sehingga menggantikan default yang dikonfigurasi untuk agen pada sesi tersebut. Aktifkan setiap prop yang Anda gunakan di bagian **Penggantian browser** untuk widget di dashboard (atau dalam `allowed_overrides` kunci). Widget baru tidak menerima penggantian apa pun, dan permintaan sesi yang membawa penggantian yang tidak aktif akan gagal dengan `400 override_not_allowed`; lihat [Penggantian browser](/id/guides/embed-web-widget).

```tsx
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%."
    />
  )
}
```

Gunakan `context` untuk memberikan agen pengetahuan faktual tentang halaman yang sedang dikunjungi pengunjung -- detail produk, harga, atau FAQ khusus halaman. Agen memperlakukannya sebagai konten halaman, bukan sebagai instruksi. Konten ini dipotong di sisi server hingga 12.000 karakter.

### Dengan Callback Peristiwa

```tsx
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}`)
      }}
    />
  )
}
```

### Dengan Gaya Kustom

```tsx
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"
    />
  )
}
```

```css
.my-custom-widget .tp-button--end {
  background-color: #e74c3c;
}
```

Lihat [panduan Styling](/id/widget/styling) untuk semua kelas CSS dan properti kustom yang tersedia.

### Dengan Nada Dering

Putar suara dering telepon saat koneksi sedang dibuat:

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

function PhoneWidget() {
  return (
    <ThunderPhoneWidget
      publishableKey="pk_live_your_publishable_key"
      ringtone={true}
    />
  )
}
```

Gunakan nada dering kustom dengan meneruskan URL file audio:

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

Nada dering berulang saat widget berada dalam status `connecting` dan memudar dengan mulus saat agen terhubung.

### Dengan Basis API Kustom

<Tip>
  Anda hanya perlu menetapkan `apiBase` jika menggunakan endpoint API yang di-host sendiri atau diproksikan. Nilai default mengarah ke `https://api.thunderphone.com/v1`.
</Tip>

```tsx
<ThunderPhoneWidget
  publishableKey="pk_live_your_publishable_key"
  apiBase="https://your-proxy.example.com/v1"
/>
```

---

## Penanganan Error

Saat callback `onError` dipanggil, callback tersebut menerima objek error dengan dua bidang:

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `error` | `string` | Kode error yang dapat dibaca mesin |
| `message` | `string` | Deskripsi error yang dapat dibaca manusia |

Kode error umum mencakup domain tidak diizinkan, agen tidak ditemukan, dan kunci API tidak valid.

---

## Langkah Selanjutnya

<CardGroup cols={2}>
  <Card title="Hook Headless" icon="code" href="/id/widget/headless-hook">
    Butuh kontrol penuh atas UI? Gunakan hook `useThunderPhone` sebagai gantinya.
  </Card>
  <Card title="Penataan Gaya" icon="palette" href="/id/widget/styling">
    Sesuaikan warna, ukuran, dan tata letak dengan properti kustom CSS.
  </Card>
</CardGroup>
