> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.vapi.ai/llms.txt.
> For full documentation content, see https://docs.vapi.ai/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.vapi.ai/_mcp/server.

# Web widget

## Overview

Add a complete AI chat and voice interface to your website with a single line of code. The Vapi Web Widget provides a customizable, floating chat interface that supports both text chat and voice conversations.

**What You'll Build:**

* Embeddable chat widget with voice and text capabilities
* Customizable themes, colors, and positioning
* Real-time conversations with context management
* Cross-platform compatibility with minimal setup

**Widget Features:**

* **Voice Mode** - Full voice conversations with transcription
* **Chat Mode** - Text-based conversations like ChatGPT
* **Custom Styling** - Match your website's design

View the complete source code and examples on [GitHub](https://github.com/VapiAI/client-sdk-react).

## Prerequisites

* A [Vapi account](https://dashboard.vapi.ai/) with a public API key
* An existing assistant or willingness to create one
* A website where you want to embed the widget

## Scenario

We'll add a customer support widget to "TechFlow's" website that allows visitors to get help through both voice and text conversations.

***

## 1. Get Your Public API Key

Go to [dashboard.vapi.ai](https://dashboard.vapi.ai) and log in to your account.

Click on your profile in the top right, then select `Vapi API Keys`.

Copy your **Public API Key**. This is safe to use in client-side code.

Unlike private keys, public keys are safe to expose in your website code.

Navigate to `Assistants` in the left sidebar and copy the ID of the assistant you want to use.

***

## 2. Install the Widget

Add the widget script to your HTML page:

```html title="index.html"
<!DOCTYPE html>
<html>
<head>
    <title>Your Website</title>
</head>
<body>
    <!-- Your website content -->
    
    <!-- Add Vapi Widget -->
    <script src="https://unpkg.com/@vapi-ai/client-sdk-react/dist/embed/widget.umd.js" async type="text/javascript"></script>
    
    <vapi-widget
      public-key="your-public-key"
      assistant-id="your-assistant-id"
      mode="chat"
      theme="light"
    ></vapi-widget>
</body>
</html>
```

Install the React package and use it as a component:

```bash title="npm"
npm install @vapi-ai/client-sdk-react
```

```bash title="yarn"
yarn add @vapi-ai/client-sdk-react
```

```bash title="pnpm"
pnpm add @vapi-ai/client-sdk-react
```

```tsx title="App.tsx"
import { VapiWidget } from '@vapi-ai/client-sdk-react';

function App() {
  return (
    <div>
      
      <VapiWidget
        publicKey="your-public-key"
        assistantId="your-assistant-id"
        mode="chat"
        theme="light"
      />
    </div>
  );
}
```

***

## 3. Configure Widget Modes

The widget supports two interaction modes:

**Voice Mode** - Voice-only conversations

```html
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="voice"
  size="compact"
></vapi-widget>
```

**Chat Mode** - Text-only conversations

```html
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="chat"
  size="full"
></vapi-widget>
```

Open your website and click the floating widget button to test the integration.

***

## 4. Customize Appearance

Customize the widget to match your website's design:

```html title="Custom Styling"
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="chat"
  theme="dark"
  size="full"
  radius="large"
  base-color="#1a1a1a"
  accent-color="#3B82F6"
  button-base-color="#000000"
  button-accent-color="#FFFFFF"
></vapi-widget>
```

Set custom text for better user experience:

```html title="Custom Labels"
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="chat"
  main-label="Chat with Support"
  start-button-text="Start Voice Chat"
  end-button-text="End Call"
  empty-chat-message="Hi! How can I help you today?"
  empty-voice-message="Click to start a voice conversation"
></vapi-widget>
```

***

## 5. Handle Events and Callbacks

Handle widget events to integrate with your application:

```html title="Event Handling"
<script>
  document.addEventListener('DOMContentLoaded', function() {
    const widget = document.querySelector('vapi-widget');
    
    // Listen for call events
    widget.addEventListener('call-start', function(event) {
      console.log('Voice call started');
      // Track analytics, update UI, etc.
    });
    
    widget.addEventListener('call-end', function(event) {
      console.log('Voice call ended');
      // Update UI, save conversation, etc.
    });
    
    widget.addEventListener('message', function(event) {
      console.log('Message received:', event.detail);
      // Process message, update state, etc.
    });
    
    widget.addEventListener('error', function(event) {
      console.error('Widget error:', event.detail);
      // Handle errors, show fallback UI, etc.
    });
  });
</script>
```

Handle events in React components:

```tsx title="React Events"
import { VapiWidget } from '@vapi-ai/client-sdk-react';

function App() {
  const handleCallStart = () => {
    console.log('Voice call started');
    // Update state, track analytics, etc.
  };
  
  const handleCallEnd = () => {
    console.log('Voice call ended');
    // Update state, save conversation, etc.
  };
  
  const handleMessage = (message: any) => {
    console.log('Message received:', message);
    // Process message, update state, etc.
  };
  
  const handleError = (error: Error) => {
    console.error('Widget error:', error);
    // Handle errors, show fallback UI, etc.
  };
  
  return (
    <VapiWidget
      publicKey="your-public-key"
      assistantId="your-assistant-id"
      mode="voice"
      onCallStart={handleCallStart}
      onCallEnd={handleCallEnd}
      onMessage={handleMessage}
      onError={handleError}
    />
  );
}
```

***

## 6. Advanced Configuration

Configure the assistant directly without pre-creating it:

The `assistant` configuration is only supported in **voice mode**. For chat mode, use `assistant-id` with optional `assistant-overrides`.

```html title="Inline Assistant Configuration"
<vapi-widget
  public-key="your-public-key"
  mode="voice"
  assistant='{
    "model": {
      "provider": "openai",
      "model": "gpt-4.1-mini",
      "messages": [
        {
          "role": "system",
          "content": "You are a helpful customer support agent for TechFlow. Be friendly and helpful."
        }
      ]
    }
  }'
></vapi-widget>
```

Modify existing assistant behavior with overrides:

```html title="Assistant Overrides"
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="chat"
  assistant-overrides='{
    "variableValues": {
      "customerName": "John Doe",
      "customerTier": "Premium"
    }
  }'
></vapi-widget>
```

Implement consent requirements for compliance:

```html title="Consent Management"
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="chat"
  require-consent="true"
  terms-content="By using this chat widget, you agree to our privacy policy and terms of service. Your conversations may be recorded for quality assurance."
  local-storage-key="techflow_widget_consent"
></vapi-widget>
```

***

## 7. Production Considerations

Consider these optimizations for production:

```html title="Performance Optimizations"
<vapi-widget
  public-key="your-public-key"
  assistant-id="your-assistant-id"
  mode="chat"
  size="compact"
  show-transcript="false"
></vapi-widget>
```

Implement proper error handling:

```javascript title="Error Handling"
document.addEventListener('DOMContentLoaded', function() {
  const widget = document.querySelector('vapi-widget');
  
  widget.addEventListener('error', function(event) {
    const error = event.detail;
    
    // Log error for debugging
    console.error('Widget error:', error);
    
    // Show user-friendly message
    if (error.message.includes('microphone')) {
      alert('Please allow microphone access to use voice features.');
    } else if (error.message.includes('network')) {
      alert('Connection error. Please check your internet connection.');
    } else {
      alert('Something went wrong. Please try again.');
    }
  });
});
```

***

## Configuration Reference

### Required Props

| Prop         | Type   | Description              |
| ------------ | ------ | ------------------------ |
| `public-key` | string | Your Vapi public API key |

### Assistant Configuration

| Prop                  | Type   | Description                                                      |
| --------------------- | ------ | ---------------------------------------------------------------- |
| `assistant-id`        | string | ID of your Vapi assistant                                        |
| `assistant`           | object | Full assistant configuration (JSON string) - **Voice mode only** |
| `assistant-overrides` | object | Override existing assistant settings (JSON string)               |

You must provide either `assistant-id`, `assistant`, or both `assistant-id` and `assistant-overrides`. The `assistant` prop is only supported in voice mode.

### Appearance Options

| Prop       | Type                                                         | Default        | Description             |
| ---------- | ------------------------------------------------------------ | -------------- | ----------------------- |
| `mode`     | `voice` \| `chat`                                            | `chat`         | Widget interaction mode |
| `theme`    | `light` \| `dark`                                            | `light`        | Color theme             |
| `position` | `bottom-right` \| `bottom-left` \| `top-right` \| `top-left` | `bottom-right` | Screen position         |
| `size`     | `tiny` \| `compact` \| `full`                                | `full`         | Widget size             |
| `radius`   | `none` \| `small` \| `medium` \| `large`                     | `medium`       | Border radius           |

### Styling Options

| Prop                  | Type   | Default   | Description                     |
| --------------------- | ------ | --------- | ------------------------------- |
| `base-color`          | string | -         | Main background color           |
| `accent-color`        | string | `#14B8A6` | Primary accent color            |
| `button-base-color`   | string | `#000000` | Floating button background      |
| `button-accent-color` | string | `#FFFFFF` | Floating button text/icon color |

### Text Customization

| Prop                  | Type   | Default        | Description                      |
| --------------------- | ------ | -------------- | -------------------------------- |
| `main-label`          | string | `Talk with AI` | Widget header text               |
| `start-button-text`   | string | `Start`        | Voice call start button text     |
| `end-button-text`     | string | `End Call`     | Voice call end button text       |
| `empty-chat-message`  | string | -              | Message when chat is empty       |
| `empty-voice-message` | string | -              | Message when voice mode is empty |

### Advanced Options

| Prop                | Type    | Default               | Description                        |
| ------------------- | ------- | --------------------- | ---------------------------------- |
| `require-consent`   | boolean | `false`               | Show consent form before first use |
| `terms-content`     | string  | -                     | Custom consent form text           |
| `local-storage-key` | string  | `vapi_widget_consent` | Key for storing consent            |
| `show-transcript`   | boolean | `true`                | Show/hide voice transcript         |

## Browser Support

* Chrome/Edge 79+
* Firefox 86+
* Safari 14.1+
* Mobile browsers with WebRTC support

## Requirements

* Microphone access for voice mode
* HTTPS required in production
* Vapi account and API key

## Next Steps

Enhance your widget integration:

* **[Chat API](/chat/quickstart)** - Build custom chat interfaces using the API directly
* **[Voice calls](/calls/outbound-calling)** - Add programmatic voice calling capabilities
* **[Custom tools](/tools/custom-tools)** - Give your assistant access to external APIs
* **[Assistant customization](/assistants)** - Fine-tune your assistant's behavior

The widget automatically handles microphone permissions, audio processing, and cross-browser compatibility. For custom implementations, consider using the [Web SDK](/quickstart/web) directly.

Need help? Chat with the team on our [Discord](https://discord.com/invite/pUFNcf2WmH) or mention us on [X/Twitter](https://x.com/Vapi_AI).