Skip to main content
The @xuna-ai/react package gives you hooks and providers to build completely custom voice interfaces in React. Use it when you need control the widget doesn’t provide — custom UI, client-side tools, dynamic overrides, or fine-grained state management.

Installation

npm install @xuna-ai/react
@xuna-ai/react re-exports everything from @xuna-ai/client, so you only need one dependency.

Minimal example

Wrap your app in ConversationProvider, then use hooks inside it to start and stop sessions.
import {
  ConversationProvider,
  useConversationControls,
  useConversationStatus,
} from '@xuna-ai/react';

function App() {
  return (
    <ConversationProvider>
      <Agent />
    </ConversationProvider>
  );
}

function Agent() {
  const { startSession, endSession } = useConversationControls();
  const { status } = useConversationStatus();

  if (status === 'connected') {
    return <button onClick={endSession}>End conversation</button>;
  }

  return (
    <button
      onClick={() =>
        startSession({ agentId: 'agent_7101k5zvyjhmfg983brhmhkd98n6' })
      }
    >
      Start conversation
    </button>
  );
}

ConversationProvider

Place ConversationProvider at the root of any component tree that needs access to conversation state. It accepts the following props:
PropTypeDescription
onConnect() => voidCalled when the session connects.
onDisconnect() => voidCalled when the session ends.
onError(error: Error) => voidCalled on session errors.
clientToolsRecord<string, Function>Tool handlers the agent can invoke on the client.
serverLocationstringRegion for the conversation server (see Server location).
isMutedbooleanControlled mute state for the microphone.
onMutedChange(muted: boolean) => voidCalled when mute state changes.

Hooks

Use granular hooks to avoid unnecessary re-renders. Each hook subscribes only to the state it needs.

useConversationControls

Session lifecycle and messaging actions.
const {
  startSession,
  endSession,
  sendUserMessage,
  sendContextualUpdate,
  setVolume,
  sendFeedback,
} = useConversationControls();
FunctionDescription
startSession(options)Start a new conversation session.
endSession()End the current session.
sendUserMessage(text)Send a text message as the user.
sendContextualUpdate(text)Inject background context without creating a visible message.
setVolume(volume)Set agent output volume (0–1).
sendFeedback(feedback)Submit thumbs-up / thumbs-down feedback for the session.

useConversationStatus

Current connection state.
const { status, message } = useConversationStatus();
// status: 'disconnected' | 'connecting' | 'connected'

useConversationInput

Microphone mute control.
const { isMuted, setMuted } = useConversationInput();

useConversationMode

Whether the agent is speaking or listening.
const { mode, isSpeaking, isListening } = useConversationMode();

useConversationFeedback

Submit session feedback.
const { canSendFeedback, sendFeedback } = useConversationFeedback();

useConversationClientTool

Register a client tool from inside a component. The tool name must match what you configured for the agent in the dashboard.
useConversationClientTool('showProductCard', (params: { productId: string }) => {
  // render a product card in your UI
  setSelectedProduct(params.productId);
  return 'Product card displayed';
});

useConversation

Combines all hooks in one. Convenient for simple components, but causes more re-renders than using granular hooks.
const conversation = useConversation();
Prefer the granular hooks (useConversationControls, useConversationStatus, etc.) in performance-sensitive components. Use useConversation only in small or infrequent-rendering components.

startSession options

Pass options to startSession to control how the session connects.
OptionDescription
agentIdAgent ID for public agents.
signedUrlSigned WebSocket URL for private agents (WebSocket auth).
conversationTokenToken for private agents (WebRTC auth).
userIdOptional end-user identifier for analytics and personalization.

Authenticating private agents

If your agent is private, generate a credential on your server and pass it to startSession. Choose WebSocket (signed URL) or WebRTC (conversation token) based on your stack.
Server
app.get('/signed-url', yourAuthMiddleware, async (req, res) => {
  const response = await fetch(
    `https://api.xuna.ai/v1/convai/conversation/get-signed-url?agent_id=${process.env.AGENT_ID}`,
    { headers: { 'xi-api-key': process.env.XUNA_AI_API_KEY } }
  );
  const body = await response.json();
  res.send(body.signed_url);
});
Client
const { startSession } = useConversationControls();

async function connect() {
  const response = await fetch('/signed-url', yourAuthHeaders);
  const signedUrl = await response.text();
  await startSession({ signedUrl });
}
Never include your XUNA_AI_API_KEY in client-side code. Always call the XUNA AI API from your server.

Client tools

Client tools let the agent call functions in your React app during a conversation — for example, to display information, submit a form, or navigate the UI. Define them on ConversationProvider or register them with useConversationClientTool.
<ConversationProvider
  clientTools={{
    displayMessage: (parameters: { text: string }) => {
      alert(parameters.text);
      return 'Message displayed';
    },
  }}
>
  <Agent />
</ConversationProvider>
The return value is sent back to the agent as the tool result.

Overrides

Pass overrides to startSession to customize agent behavior for a specific session without changing the agent’s dashboard configuration.
await startSession({
  agentId: 'agent_7101k5zvyjhmfg983brhmhkd98n6',
  overrides: {
    agent: {
      prompt: { prompt: 'You are a helpful assistant for Acme Corp.' },
      firstMessage: 'Hi! How can I help you today?',
      language: 'en',
    },
    tts: {
      voiceId: 'your_custom_voice_id',
    },
  },
});

Server location

By default, conversation servers are located in the US. Set serverLocation on ConversationProvider to use a different region.
ValueRegion
"us"United States (default)
"eu-residency"European Union
"in-residency"India
"global"Closest available region
<ConversationProvider serverLocation="eu-residency">
  <Agent />
</ConversationProvider>

Next steps

  • To deploy without a custom UI, use the widget instead.
  • For mobile apps, see the Mobile SDKs page.
  • To build a fully custom integration outside React, use the WebSocket API.