---
title: "Thành phần React"
description: "Nhúng tiện ích giọng nói ThunderPhone vào ứng dụng React"
---

Component `ThunderPhoneWidget` hiển thị thanh cuộc gọi hiệu ứng glassmorphism với các nút điều khiển tích hợp để tắt tiếng, kết thúc cuộc gọi và hiển thị trạng thái kết nối. Đây là cách nhanh nhất để thêm AI giọng nói vào ứng dụng React.

## Cài đặt

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

## Cách dùng cơ bản

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

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

<Warning>
  Bạn **phải** import tệp CSS để widget hiển thị đúng. Nếu không, widget sẽ không có kiểu dáng.
</Warning>

---

## Thuộc tính

Component chấp nhận các thuộc tính sau thông qua `ThunderPhoneWidgetProps`:

| Thuộc tính | Kiểu | Bắt buộc | Mặc định | Mô tả |
|------|------|----------|---------|-------------|
| `publishableKey` | `string` | Có | -- | Khóa API có thể công khai (`pk_live_...`) từ **Web Widgets** trong dashboard. Tác nhân AI được xác định tự động từ cấu hình widget của khóa. |
| `theme` | `'light' \| 'dark'` | Không | `'light'` | Bảng màu. Áp dụng lớp `tp--light` hoặc `tp--dark` cho phần tử gốc của widget. |
| `primaryColor` | `string` | Không | `'#000000'` (sáng) / `'#ffffff'` (tối) | Chuỗi màu CSS dùng làm màu nhấn (nút gọi, dạng sóng, chỉ báo đang hoạt động). |
| `title` | `string` | Không | `'Voice assistant'` | Văn bản hiển thị trên thanh widget. |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | Không | `'bottom-right'` | Vị trí cố định trong khung nhìn của widget. |
| `apiBase` | `string` | Không | `'https://api.thunderphone.com/v1'` | Ghi đè URL cơ sở API. |
| `language` | `string` | Không | -- | Ghi đè ngôn ngữ theo phiên -- mã ngôn ngữ hoặc ngôn ngữ khu vực như `en`, `es` hoặc `fr-FR`. Khi không đặt, ngôn ngữ đã cấu hình của tác nhân AI sẽ được áp dụng. Yêu cầu `language` trong [ghi đè trình duyệt](/vi/guides/embed-web-widget) của khóa. |
| `voice` | `string` | Không | -- | Ghi đè giọng nói theo phiên -- tên giọng nói như `maria`. Khi không đặt, giọng nói đã cấu hình của tác nhân AI sẽ được áp dụng. Yêu cầu `voice` trong [ghi đè trình duyệt](/vi/guides/embed-web-widget) của khóa. |
| `context` | `string` | Không | -- | Ngữ cảnh thực tế của trang hoặc trang web theo phiên được truyền cho tác nhân AI (ví dụ: thông tin chi tiết về trang khách truy cập đang xem). Bị cắt ngắn ở phía máy chủ còn 12.000 ký tự. Yêu cầu `context` trong [ghi đè trình duyệt](/vi/guides/embed-web-widget) của khóa. |
| `onConnect` | `() => void` | Không | -- | Được gọi khi phiên giọng nói kết nối thành công. |
| `onDisconnect` | `() => void` | Không | -- | Được gọi khi phiên kết thúc. |
| `onError` | `(error) => void` | Không | -- | Được gọi khi xảy ra lỗi. Đối tượng `error` có các trường `error` (mã) và `message`. |
| `className` | `string` | Không | -- | Tên lớp CSS bổ sung được áp dụng cho vùng chứa widget. |
| `ringtone` | `boolean \| string` | Không | `false` | Phát nhạc chuông trong khi kết nối. `true` để dùng nhạc chuông mặc định hoặc chuỗi URL cho âm thanh tùy chỉnh. |

---

## Ví dụ

### Giao diện tối với màu tùy chỉnh

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

### Vị trí tùy chỉnh

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

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

### Ngôn ngữ, giọng nói và ngữ cảnh theo từng phiên

Các prop `language`, `voice` và `context` được chuyển tiếp đến yêu cầu phiên (`POST /widget/session`) khi cuộc gọi bắt đầu, ghi đè các giá trị mặc định đã cấu hình của tác nhân AI cho phiên đó. Bật từng prop bạn sử dụng trong **Ghi đè trình duyệt** cho widget trên dashboard (hoặc trong `allowed_overrides` của key). Widget mới không chấp nhận prop nào và yêu cầu phiên chứa một prop đang tắt sẽ thất bại với `400 override_not_allowed`; xem [Ghi đè trình duyệt](/vi/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%."
    />
  )
}
```

Sử dụng `context` để cung cấp cho tác nhân AI kiến thức thực tế về trang mà khách truy cập đang xem -- chi tiết sản phẩm, giá hoặc Câu hỏi thường gặp dành riêng cho trang. Tác nhân AI xem đây là nội dung trang, không phải hướng dẫn. Nội dung này được cắt ngắn ở phía máy chủ còn 12.000 ký tự.

### Với callback sự kiện

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

### Với kiểu dáng tùy chỉnh

```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;
}
```

Xem [hướng dẫn kiểu dáng](/vi/widget/styling) để biết tất cả lớp CSS và thuộc tính tùy chỉnh có sẵn.

### Với nhạc chuông

Phát âm thanh chuông điện thoại trong khi thiết lập kết nối:

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

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

Dùng nhạc chuông tùy chỉnh bằng cách truyền URL của tệp âm thanh:

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

Nhạc chuông lặp lại khi widget ở trạng thái `connecting` và mờ dần một cách mượt mà khi tác nhân AI kết nối.

### Với API Base tùy chỉnh

<Tip>
  Bạn chỉ cần đặt `apiBase` nếu đang dùng điểm cuối API tự lưu trữ hoặc proxy. Giá trị mặc định trỏ đến `https://api.thunderphone.com/v1`.
</Tip>

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

---

## Xử lý lỗi

Khi callback `onError` được gọi, callback này nhận một đối tượng lỗi có hai trường:

| Trường | Loại | Mô tả |
|-------|------|-------------|
| `error` | `string` | Mã lỗi mà máy có thể đọc |
| `message` | `string` | Mô tả lỗi mà con người có thể đọc |

Các mã lỗi phổ biến gồm tên miền không được phép, không tìm thấy tác nhân AI và khóa API không hợp lệ.

---

## Bước tiếp theo

<CardGroup cols={2}>
  <Card title="Hook không giao diện" icon="code" href="/vi/widget/headless-hook">
    Cần toàn quyền kiểm soát UI? Hãy dùng hook `useThunderPhone`.
  </Card>
  <Card title="Tạo kiểu" icon="palette" href="/vi/widget/styling">
    Tùy chỉnh màu sắc, kích thước và bố cục bằng thuộc tính tùy chỉnh CSS.
  </Card>
</CardGroup>
