> ## Documentation Index
> Fetch the complete documentation index at: https://docs.outspeed.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React SDK Example

> Build your first voice AI application with the Outspeed React SDK

## Basic Voice Chat Component

Here's a complete example of a React component that creates a voice conversation using the `useConversation` hook:

```tsx theme={null}
import React, { useState } from "react";
import { type SessionConfig } from "@outspeed/client";
import { useConversation } from "@outspeed/react";

const getEphemeralKeyFromServer = async (config: SessionConfig) => {
  const tokenResponse = await fetch("/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(config),
  });

  const data = await tokenResponse.json();
  if (!tokenResponse.ok) {
    throw new Error("Failed to get ephemeral key");
  }

  return data.client_secret.value;
};

const sessionConfig: SessionConfig = {
  model: "outspeed-v1",
  instructions: "You are a helpful but witty assistant named Alfred.",
  voice: "david", // see the voices page for all available voices
  turn_detection: {
    type: "semantic_vad",
  },
  first_message: "Hello, how can I assist you with Outspeed today?",
};

export default function VoiceChat() {
  const [sessionCreated, setSessionCreated] = useState(false);
  const [isConnecting, setIsConnecting] = useState(false);

  const conversation = useConversation({
    sessionConfig: sessionConfig,
  });

  const startSession = async () => {
    try {
      setIsConnecting(true);
      const ephemeralKey = await getEphemeralKeyFromServer(sessionConfig);
      await conversation.startSession(ephemeralKey, {
        source: "react-example" // Optional: custom string to identify session source
      });

      // Listen for session creation event
      conversation.on("session.created", (event) => {
        console.log("Session created:", event);
        setSessionCreated(true);
        setIsConnecting(false);
      });
    } catch (error) {
      console.error("Error starting session:", error);
      setIsConnecting(false);
    }
  };

  const endSession = async () => {
    try {
      await conversation.endSession();
      setSessionCreated(false);
    } catch (error) {
      console.error("Error ending session:", error);
    }
  };

  if (isConnecting) {
    return (
      <div className="voice-chat">
        <h2>Connecting...</h2>
        <p>Please wait while we establish the connection.</p>
      </div>
    );
  }

  if (sessionCreated) {
    return (
      <div className="voice-chat">
        <h2>🎙️ Voice Chat Active</h2>
        <p>You can now speak with the AI assistant!</p>
        <button onClick={endSession} className="end-button">
          End Session
        </button>
      </div>
    );
  }

  return (
    <div className="voice-chat">
      <h2>Voice AI Assistant</h2>
      <p>Click the button below to start a voice conversation.</p>
      <button onClick={startSession} className="start-button">
        Start Voice Chat
      </button>
    </div>
  );
}
```

<Note>Want to use a different voice? See all [available voices](/features/voices) you can choose from.</Note>

## Connection Management

### onDisconnect Callback

Use `onDisconnect` to handle cleanup when the conversation ends:

```tsx theme={null}
const conversation = useConversation({
  onDisconnect: () => {
    console.log("Disconnected! cleaning up...");
    setSessionCreated(false);
    // Add any cleanup logic here
  },
});
```

**Important**: Use `onDisconnect` for cleanup logic, not `onError`. The `onError` callback is called for runtime errors as well, so it's not ideal for cleanup tasks.

### onError Callback

Use `onError` specifically for handling errors:

```tsx theme={null}
const conversation = useConversation({
  onError: (err) => {
    console.error("Conversation error:", err);
    // Handle error display/logging only
  },
  onDisconnect: () => {
    console.log("Session ended");
    setSessionCreated(false);
    // Handle cleanup here instead
  },
});
```

## Advanced Features

### Event Handling

You can listen to various events during the conversation:

```tsx theme={null}
// Listen for speech detection
conversation.on("input_audio_buffer.speech_started", () => {
  console.log("User started speaking");
});

conversation.on("input_audio_buffer.speech_stopped", () => {
  console.log("User stopped speaking");
});

// Listen for AI responses
conversation.on("response.text.delta", (event) => {
  console.log("AI response text:", event.delta);
});

conversation.on("response.audio_transcript.delta", (event) => {
  console.log("AI speech transcript:", event.delta);
});
```

### Text Input

You can also send text messages programmatically:

```tsx theme={null}
const sendTextMessage = () => {
  conversation.sendText("Tell me about the weather today");
};

return (
  <div>
    {/* ... other components */}
    <button onClick={sendTextMessage}>Send Text Message</button>
  </div>
);
```

### Mute Control

Control the microphone state by passing `micMuted` prop to `useConversation`:

```tsx theme={null}
const [isMicMuted, setIsMicMuted] = useState(false);

const conversation = useConversation({
  micMuted: isMicMuted,
});

const toggleMute = () => {
  setIsMicMuted(!isMicMuted);
};

return (
  <div>
    {/* ... other components */}
    <button onClick={toggleMute}>{isMicMuted ? "🔇 Unmute" : "🎤 Mute"}</button>
  </div>
);
```

### Volume Control

Control the AI's voice volume by passing `volume` prop to `useConversation` (value between 0 and 1):

```tsx theme={null}
const [volume, setVolume] = useState(0.8);

const conversation = useConversation({
  volume: volume,
});

return (
  <div>
    {/* ... other components */}
    <label>
      Volume: {Math.round(volume * 100)}%
      <input
        type="range"
        min="0"
        max="1"
        step="0.1"
        value={volume}
        onChange={(e) => setVolume(parseFloat(e.target.value))}
      />
    </label>
  </div>
);
```

## Complete Example with Error Handling

Here's a more complete example showing proper error and disconnect handling:

```tsx theme={null}
const [error, setError] = useState<string | null>(null);
const [sessionCreated, setSessionCreated] = useState(false);

const conversation = useConversation({
  onError: (err) => {
    console.error("Conversation error:", err);
    setError(err.message);
    // Only handle error display here
  },
  onDisconnect: () => {
    console.log("Session ended");
    setSessionCreated(false);
    setError(null); // Clear any previous errors
    // Handle all cleanup logic here
  },
});

// Display error to user
if (error) {
  return (
    <div className="error-state">
      <h2>❌ Error</h2>
      <p>{error}</p>
      <button onClick={() => setError(null)}>Try Again</button>
    </div>
  );
}
```

## Next Steps

* [API Reference](/api-spec/client) - Explore all available methods and events
* [Starter Templates](/get-started/templates) - See template implementations
