@voxide/react
v0.8.0
Published
Capability-first voice AI SDK for the web. Register your JavaScript functions and let a live voice agent call them when users speak or type.
Maintainers
Readme
@voxide/react
Capability-first voice AI for any website. Describe what your app can do as plain JavaScript functions, and a live voice agent decides when and how to call them from what your users say or type — no intents, no dialog trees, no string matching.
npm install @voxide/react@latestShips fully-typed React components (React 18 or 19). Works in Next.js, Vite, CRA, Remix — anything that renders React in the browser.
60-second quick start
"use client";
import { VoxideClient, VoxideWidget } from "@voxide/react";
// 1. Create a client with your publishable key (from the Voxide dashboard).
const ai = new VoxideClient({ publicKey: "vox_pub_..." });
// 2. Tell the agent what your app can do.
ai.register({
addToCart: {
description: "Add an item to the shopping cart.",
params: { itemId: { type: "string", required: true }, qty: { type: "number" } },
handler: async ({ itemId, qty = 1 }) => {
await fetch("/api/cart", { method: "POST", body: JSON.stringify({ itemId, qty }) });
return { status: "ok" };
},
},
});
// 3. Drop the widget in. It initialises itself — no useEffect, no loading flag.
export default function App() {
return <VoxideWidget client={ai} />;
}That's it. A launcher appears in the corner. <VoxideWidget> calls ai.init() for you, shows a "Connecting…" state while it loads, and a clear error inside the panel if something goes wrong.
Core concepts
- Capability-first — you register what your app can do; the model matches intent to capability. Add a function, and the agent can use it immediately.
- Hosted backend — the SDK talks only to the Voxide backend. Your publishable key is safe to ship in the browser; it never touches Google directly.
- Manifest sync — on
init()the SDK uploads a snapshot of your registered actions so you can inspect, version, and lock them from the dashboard. - Live state —
ai.bindState(() => ({ cart, page }))lets the agent always see the current UI without you stuffing prompts. - Real results — whatever your
handlerreturns is fed back to the model, so acheckStocktool can answer "yes, 3 left" out loud.
Before it works: allow your domain
The SDK runs in your users' browsers and calls the Voxide backend cross-origin.
- Open your project in the Voxide dashboard.
- Add the domain(s) where you embed the widget to the domain whitelist (e.g.
app.yoursite.com). localhostis always allowed, so local dev needs no setup.
If a domain isn't whitelisted, the widget shows "Assistant unavailable" and the console logs a domain/CORS error.
The widget
<VoxideWidget
client={ai}
theme="auto" // "light" | "dark" | "auto" (default: auto)
accentColor="#FF6600" // your brand colour
position="bottom-right" // or "bottom-left"
title="Ask Acme" // header label (defaults to the agent name)
/>The panel has a Text and a Voice tab. Voice streams mic audio to the agent and plays its reply; text is a normal chat box. Switching to Voice auto-connects the mic; closing the panel releases it.
Prefer your own UI? Use the hook:
import { useVoxideVoice } from "@voxide/react";
function MyMic() {
const { status, messages, connect, disconnect, sendText } = useVoxideVoice(ai);
// ...render whatever you like
}API
new VoxideClient(config)
| option | type | notes |
|---|---|---|
| publicKey | string | required — your vox_pub_... key. |
| baseUrl | string | Override the Voxide backend (self-hosting). You normally omit this. |
| language | string | ISO code, e.g. "en-US". |
| ui | VoxideUIConfig | Default widget look: accentColor, position, theme, title. |
ai.register(actions)
ai.register({
bookTable: {
description: "Reserve a table.",
params: { guests: { type: "number", required: true }, time: { type: "string", required: true } },
scope: "global", // or a route prefix like "/restaurant" (or "/shop/*")
dangerous: false, // if true, the user is asked to confirm first
handler: async (args) => { /* ... */ },
},
});Protecting personal data — sensitive: true
If a visitor speaks their name, phone number or address to fill a form, that
value would otherwise be stored with the conversation. Mark the parameter
sensitive and Voxide replaces it with [redacted] before writing it to the
database — so it is never visible in your dashboard, never visible to Voxide,
and not present in any backup.
ai.register({
fillContactForm: {
description: "Fill in the contact form.",
params: {
name: { type: "string", sensitive: true },
phone: { type: "string", sensitive: true },
topic: { type: "string" },
},
// Your handler still receives the real values — only storage is affected.
handler: async ({ name, phone, topic }) => submitForm({ name, phone, topic }),
},
});Emails, phone numbers, card numbers, national IDs and IBANs are already
detected and removed from transcripts automatically. Pattern matching can't
reliably recognise a spoken name or street address, which is exactly what
sensitive: true is for. Redaction is permanent and cannot be undone.
ai.bindState(getter)
Expose current UI state to the agent every turn:
ai.bindState(() => ({ cart: getCart(), currentPage: location.pathname }));ai.setActiveRoute(path)
Scope which actions are callable on the current page. Actions with scope: "/checkout" only surface there; "/shop/*" matches any sub-route.
ai.setUser({ userId, email })
Identify the end-user so memory can persist across sessions.
ai.use((ctx, next, cancel) => ...)
Middleware for validation, logging, rate-limiting, or blocking a call. cancel() stops execution.
ai.onConfirmation(handler)
Replace the default window.confirm for dangerous actions with your own modal:
ai.onConfirmation(async (action, args) => myModal.confirm(action.description));ai.enableNavigation(router, routes?)
Auto-registers a navigate action wired to your router (Next.js useRouter, React Router, etc.).
Pass your real routes. Without them the agent has to guess a path from what the
user said, so "take me to the near-me page" becomes /nearMe when your route is
/near — a 404. Given the list, the model is constrained to your exact paths, and an
off-list path is refused instead of navigating to a dead page.
ai.enableNavigation(router, [
{ path: "/near", description: "Spots near the user" },
{ path: "/saved", description: "The user's saved spots" },
]);
// shorthand:
ai.enableNavigation(router, { "/near": "Spots near me", "/saved": "Saved spots" });ai.on(event, cb)
Subscribe to "action" | "status" | "message" | "transcript" | "ready" | "error". Returns an unsubscribe function.
ai.init()
Optional — <VoxideWidget> calls it for you. Idempotent and safe to call multiple times. Call it manually only if you want to initialise before the widget mounts.
TypeScript
Everything is typed. Import helper types directly:
import type { VoxideAction, VoxideStatus, VoxideUIConfig } from "@voxide/react";License
MIT © Voxide
