@monoverse/voicebot-react
v0.8.0
Published
React wrapper for the VoiceBot talking widget — drop the deployed pk_ embed onto a React or Next.js app.
Maintainers
Readme
@monoverse/voicebot-react
A tiny, production-grade React wrapper for the VoiceBot talking widget. Drop a voice (or chat) assistant onto your React or Next.js app with one component and your public key.
This package is a thin injector over the already-deployed VoiceBot browser embed. It loads
the CDN bundle, hands it your pk_ public key, and the deployed widget self-bootstraps:
exchanges the pk_ (+ the browser-set Origin) for an origin-locked session token and mounts,
grounded in your store's catalog. There is no widget code in this package and no merchant
backend is required.
Install
npm install @monoverse/voicebot-reactreact and react-dom are peer dependencies (17, 18, or 19).
Usage
Render <VoiceBotWidget> once, near the root of your app.
import { VoiceBotWidget } from '@monoverse/voicebot-react';
export function App() {
return (
<>
<YourApp />
<VoiceBotWidget publicKey="pk_live_xxx" />
</>
);
}Voice or chat
<VoiceBotWidget publicKey="pk_live_xxx" variant="voice" /> {/* default */}
<VoiceBotWidget publicKey="pk_live_xxx" variant="chat" />voice loads widget-voice.js; chat loads widget-chat.js.
Props
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ------------------------------------ | ------------------------------------------------------------------ |
| publicKey | string (required) | — | Your pk_ publishable key. |
| variant | 'voice' \| 'chat' | 'voice' | Which widget bundle to load. |
| scriptSrc | string | https://api.monoverse.tech/widget | Override the CDN base (self-hosted API). Also sets data-api-base.|
| onError | (error: Error) => void | — | Called on script load failure or an empty publicKey. |
Reactivity
The VoiceBot widget core is a one-per-page singleton — one mounted instance owns a single session token, WebSocket, and microphone. The wrappers document one consistent model:
- This package (React) re-injects — tears the instance down, then re-mounts — when
publicKey,variant, orscriptSrcchange.onErroris not a dependency: an inline callback is a new identity every render and must never drive a re-inject. - The Vue and Next wrappers are one-shot: they read props at mount and do not
re-inject on a prop change (Vue dedupes the singleton; Next's
next/scriptcaches the tag bysrc). To switch the key at runtime there, remount the host component.
Hook (imperative)
For custom layouts, useVoiceBot runs the same lifecycle without rendering an element:
import { useVoiceBot } from '@monoverse/voicebot-react';
function WidgetMount() {
useVoiceBot({ publicKey: 'pk_live_xxx', variant: 'chat' });
return null;
}Next.js
The component is SSR-safe (it injects the script in an effect and renders null on the server),
so it works in both the App Router and the Pages Router. In the App Router, mark the host as a
client boundary and render once in the root layout:
'use client';
import { VoiceBotWidget } from '@monoverse/voicebot-react';
export function VoiceBot() {
return <VoiceBotWidget publicKey="pk_live_xxx" />;
}You can also use Next's own next/script directly (see the
universal embed guide); this package exists so
you do not have to.
Provisioning your pk_
pk_ is issued server-side by a VoiceBot operator (a repo script — seed_public_key.py).
When the key is issued, you list the exact origins allowed to use it:
- List every origin explicitly.
https://shop.example,https://www.shop.example, andhttps://app.shop.exampleare three different origins. wwwand subdomains are not inherited — register each separately.- An origin is
scheme://host[:port].http≠https; a non-default dev port is part of the origin (e.g.http://localhost:5173). Register your dev origins too. - Wildcards are not supported.
If the widget does not appear, the current origin is almost always missing from the key's
allow-list — the token mint returns 403 and the widget silently does not mount (your page is
never broken). See the provisioning guide.
Security
pk_ is a public, origin-locked key — exactly like a Stripe publishable key. Shipping it in
your client bundle is safe and intended:
- The mint endpoint issues a token only if the browser-set
Originis in the key's allow-list; another site copying yourpk_gets a403. tenant_idis resolved server-side from thepk_and signed into the token claim — the client never sends it, so cross-tenant access is structurally impossible.- The WebSocket upgrade re-checks
Originagainst the token claim and closes (4403) on mismatch.
The paired secret sk_ (catalog/order sync HMAC) is never in the browser — that is a
separate server-to-server flow, unrelated to this package.
Scope: what works now vs. the next tier
Works now with this package — a consultant grounded in your catalog: it answers product questions, recommends items, and describes your shipping and payment options from your store data.
Next tier (not in this package) — first-party actions: routing add_to_cart,
navigation, or other writes into your own app state. That needs the handler-registration SDK
(createWidget + merchant executors, ADR-053 / T5), which is a separate, heavier package and is
out of scope here. This wrapper is a script injector, not an action bridge.
License
MIT
