> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/quickstart.md).

# Web SDK quickstart

Build a React page that connects to a Convai character, sends it a text message, and shows the reply, using the Convai Web SDK from npm.

In this quickstart we will connect a React page to a Convai character with the Convai Web SDK, send the character a message, and show its reply. By the end, you will have a page with a **Connect** button and a running conversation.

### What we will build

We will build one React component that creates a client with `useConvaiClient`, connects when you select **Connect**, sends a greeting, and lists the messages from both sides. The character answers in text and, by default, in speech.

### Prerequisites

* A React 18 or 19 project with a bundler.
* A Convai account and an API key from <code class="expression">space.vars.dashboard\_url</code>.
* The ID of a character you created in Convai.

### Install the SDK

Install `@convai/web-sdk`, release <code class="expression">space.vars.web\_sdk\_version</code> or later:

```bash
npm install @convai/web-sdk
```

### Create the client

We create the client once per component with `useConvaiClient`. The config is stored and used when we call `connect()`. `endUserId` is a stable ID for the person using the page; Convai uses it for long-term memory and analytics. `AudioRenderer`, inside an `AudioContext` provider that receives `client.room`, plays the character's voice.

{% code title="src/App.tsx" %}

```tsx
import { useConvaiClient, AudioRenderer, AudioContext } from '@convai/web-sdk/react';

export default function App() {
  const client = useConvaiClient({
    apiKey: 'YOUR_API_KEY',
    characterId: 'YOUR_CHARACTER_ID',
    endUserId: 'user-123',
  });

  const handleConnect = async () => {
    try {
      await client.connect();
    } catch (err) {
      // connect() rejects with the reason Convai returned.
      console.error('Connect failed:', err);
    }
  };

  const handleSend = () => {
    client.sendUserTextMessage('Hello. Who are you?');
  };

  return (
    <AudioContext.Provider value={client.room}>
      {/* Plays the character's voice */}
      <AudioRenderer />
      <p>Status: {client.activity}</p>

      {!client.state.isConnected ? (
        <button onClick={handleConnect}>Connect</button>
      ) : (
        <button onClick={handleSend} disabled={!client.isBotReady}>
          Say hello
        </button>
      )}

      <ul>
        {client.chatMessages
          .filter(
            (m) =>
              m.type === 'user-llm-text' ||
              m.type === 'user-transcription' ||
              m.type === 'bot-llm-text' ||
              m.type === 'bot-output',
          )
          .map((m) => (
            <li key={m.id}>
              <strong>{m.type.startsWith('user') ? 'You' : 'Character'}:</strong> {m.content}
            </li>
          ))}
      </ul>
    </AudioContext.Provider>
  );
}
```

{% endcode %}

The status line shows `client.activity`, a string the hook keeps up to date: `"Idle"` before the first connection, then `"Connecting..."`, `"Connected"`, `"Listening"`, `"Thinking"`, `"Speaking"`, or `"Disconnected"`.

### Run the conversation

Start your dev server and open the page. Select **Connect** and wait until the **Say hello** button is enabled. The button stays disabled until `client.isBotReady` is `true`, the same check `ConvaiWidget` applies before it sends text.

Select **Say hello**. Your message appears in the list straight away as a `user-llm-text` message, and the character's reply streams in as a `bot-llm-text` message.

{% hint style="success" %}
The status changes to `Thinking` and then `Speaking`, the reply text appears under your message, and you hear the character's voice.
{% endhint %}

{% hint style="danger" %}
This page ships your API key to the browser. Use it for local development only. For a page other people can open, create a short-lived auth token on your server and pass `authToken` instead. See [Connect with an auth token](/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/auth-tokens.md).
{% endhint %}

### Next steps

{% content-ref url="/pages/QARm23s6FuiamQWrYu2J" %}
[Get started with React](/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/react.md)
{% endcontent-ref %}

{% content-ref url="/pages/mskfPzpQqd1cn7uDGJGm" %}
[Get started with vanilla TypeScript](/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/vanilla-typescript.md)
{% endcontent-ref %}

{% content-ref url="/pages/CtmoIx5N2jgYRITcb48k" %}
[Drive facial animation with blendshapes](/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/lipsync-and-blendshape.md)
{% endcontent-ref %}

{% content-ref url="/pages/QOOOI31YjB7Tih8ktqsx" %}
[Handle errors and disconnects](/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/error-handling.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/plugins-and-integrations/web-plugins/convai-web-sdk/quickstart.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
