How to add a voice assistant to a React app
Add a voice + text agent to a React app in a few minutes (Next.js App Router shown; works in Vite, Remix, CRA too). Want to skip the boilerplate? Let your AI coding assistant set it up — or watch the guide video, our founder doing it in one prompt.
#1. Install
One typed package. React 18 or 19. @latest always pulls the newest release, even if an older version is already installed.
npm install @voxide/react@latestReact is what the widget on this page needs, not what Voxide needs. Vue, Svelte, Angular, Django, Rails and plain HTML run the same client through @voxide/react/core or a script tag, headless and with no prebuilt UI. See Other Frameworks.
#2. Create the assistant
In a Client Component, create a client with your publishable key, then register capabilities, each is a plain-English description, typed params, and a handler that runs your real code. The widget initialises itself, so there's no useEffect or loading flag.
"use client";import { VoxideClient, VoxideWidget } from "@voxide/react"; const ai = new VoxideClient({ publicKey: "vox_pub_..." }); 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" }; }, },}); export function Assistant() { // Pass nothing but the client. Every other prop outranks the dashboard, so // hardcoding one makes the matching control in Appearance silently do // nothing. Set the colour, title and placement there instead. return <VoxideWidget client={ai} />;}<VoxideWidget> accepts props for the accent colour, title, position and theme, and every one of them outranks whatever the Appearance tab says. That is deliberate, so a value pinned to a build cannot shift under you, but it means a hardcoded prop makes the matching dashboard control look broken. Pass one only when you want exactly that. From version 0.7.5 the SDK logs a console warning naming any prop that is shadowing a dashboard value.#3. Mount it once, in the true root
Render the assistant in the component that wraps every route, the root layout, not a page. This is the one step worth double-checking: a call in progress survives navigation and theme changes automatically, but only if the widget itself never unmounts. If it's placed on a specific page (or a nested layout that doesn't cover the whole app), the widget remounts on navigation and any active call gets cut off.
import { Assistant } from "@/components/Assistant"; export default function RootLayout({ children }) { return ( <html> <body> {children} <Assistant /> </body> </html> );}Vite/CRA: mount it in the top-level <App />, as a sibling of your router. Pages Router: mount it in pages/_app.tsx. Remix/React Router: the root route with the outermost <Outlet />.
localhost always works. Your vox_pub_… key lives in each project's Integration tab (already pre-filled in the copy snippet there).