@zeroweight/react
v0.2.39
Published
React hooks & components for the ZeroWeight avatar renderer. Drop-in LiveKit-powered avatar sessions.
Maintainers
Readme
@zeroweight/react
React hooks and components for the ZeroWeight avatar renderer. Drop-in LiveKit-powered avatar sessions for React and Next.js.
Installation
npm install @zeroweight/react @zeroweight/rendererPeer Dependencies
The following are required in your project:
npm install react react-domLiveKit, lucide-react, and other dependencies are installed automatically.
Quick Start
Drop-in Component (Simplest)
import { LiveKitAvatarSession } from "@zeroweight/react";
function App() {
return (
<LiveKitAvatarSession
avatarId="your-avatar-id"
apiKey="your-api-key"
sessionDuration={120}
inactivityTimeout={30000}
turnOffMicWhenAISpeaking
showBorder={false}
/>
);
}This renders a full avatar session with canvas, controls, status badge, and LiveKit voice chat using the built-in defaults:
livekitUrl:wss://prod-project-pazuyq69.livekit.cloud- API base URL:
https://api.zeroweight.ai/api/v1 - auth header:
X-ZW-Api-Key: <apiKey>only whenapiKeyis provided
That makes it easy to quickly test by pasting an avatarId and apiKey.
You can also tune session timing directly on LiveKitAvatarSession with sessionDuration and inactivityTimeout, just like useAvatarSession.
Custom UI (Full Control)
import { useAvatarSession, LiveKitAvatarProvider } from "@zeroweight/react";
import { LiveKitRoom } from "@livekit/components-react";
function CustomAvatar() {
const session = useAvatarSession({
avatarId: "your-avatar-id",
apiKey: "optional-api-key",
livekitUrl: "wss://...",
api: { getBundle, getLiveKitToken },
turnOffMicWhenAISpeaking: true,
});
return (
<div>
{/* Canvas container — renderer injects <canvas> here */}
<div ref={session.containerRef} style={{ width: 640, height: 480 }} />
{/* Your own UI */}
<p>State: {session.rendererState}</p>
<button onClick={session.connect}>Connect</button>
<button onClick={session.disconnect}>Disconnect</button>
<button onClick={() => session.runAction("wave_hand")}>Wave</button>
{/* LiveKit room for voice (hidden) */}
{session.token && (
<LiveKitRoom
serverUrl={session.livekitUrl}
token={session.token}
connect
audio
onConnected={session.markConnected}
onDisconnected={session.disconnect}
>
<LiveKitAvatarProvider session={session} />
</LiveKitRoom>
)}
</div>
);
}API
ZeroWeightApi Interface
You can provide your own backend integration via this interface:
interface ZeroWeightApi {
getBundle: (avatarId: string) => Promise<{ payload: any }>;
getLiveKitToken: (
avatarId: string,
userName: string,
) => Promise<{ token: string }>;
}useAvatarSession(config)
The core React hook. Manages the renderer lifecycle and returns reactive state + imperative methods.
Config:
| Prop | Type | Required | Description |
| ------------------- | --------------- | -------- | ---------------------------------------------- |
| avatarId | string | ✅ | Avatar ID to load |
| apiKey | string \| null| – | Optional API key for the built-in API |
| api | ZeroWeightApi | – | Custom API implementation |
| turnOffMicWhenAISpeaking | boolean | – | Auto-mute when AI speaks, then unmute after (default: true) |
| livekitUrl | string | – | LiveKit server URL |
| sessionDuration | number | – | Session limit in seconds (default: 120) |
| inactivityTimeout | number | – | Auto-disconnect timeout in ms (default: 30000) |
Default behavior when you do not pass api or livekitUrl:
livekitUrldefaults towss://prod-project-pazuyq69.livekit.cloudgetBundle()callshttps://api.zeroweight.ai/api/v1/avatars/bundle/:avatarIdgetLiveKitToken()callshttps://api.zeroweight.ai/api/v1/livekit/getToken?avatar_id=:avatarId&name=:randomNameX-ZW-Api-Keyis attached only ifapiKeyis non-null
For production use, the recommended setup is still to keep your API key on your server and pass a custom api object that calls your own backend endpoints.
Returns:
| Property | Type | Description |
| --------------- | ----------- | -------------------------------------------------------- |
| containerRef | RefObject | Attach to a <div> — renderer creates <canvas> inside |
| rendererState | string | "idle" | "loading" | "ready" | "error" |
| isEngineReady | boolean | true when renderer is ready |
| isConnected | boolean | LiveKit connection status |
| connect() | function | Start voice session |
| disconnect() | function | End voice session |
| runAction(id) | function | Trigger an avatar action |
| toggleMic() | function | Toggle microphone mute |
| setVolume(v) | function | Set audio volume (0–1) |
LiveKitAvatarSession Props
In addition to all useAvatarSession config props above, the drop-in component accepts:
| Prop | Type | Default | Description |
| ------------------- | --------------- | ------- | ---------------------------------------------- |
| showBorder | boolean | true | Show border and shadow around the container |
| style | CSSProperties | – | Style overrides for the outer section |
| className | string | – | Class name overrides for the outer section |
| loadingContent | ReactNode | – | Custom loading UI for the canvas |
| customControls | function | – | Replace default controls (session) => ReactNode |
| customStatusBadge | function | – | Replace default status badge (session) => ReactNode |
Components
| Component | Description |
| ----------------------- | -------------------------------------------------- |
| LiveKitAvatarSession | Full drop-in: canvas + controls + LiveKit |
| AvatarCanvas | Canvas container with loading overlay |
| AvatarControls | Default mic/connect buttons |
| AvatarStatusBadge | Online/offline status indicator |
| LiveKitAvatarProvider | LiveKit ↔ renderer bridge (inside <LiveKitRoom>) |
All components accept a session prop from useAvatarSession().
Integration Example
Adapting an existing API service:
import { api } from "./my-api";
import type { ZeroWeightApi } from "@zeroweight/react";
const zeroWeightApi: ZeroWeightApi = {
getBundle: (avatarId) => api.getBundle(avatarId),
getLiveKitToken: (_avatarId, userName) => api.getToken(userName),
};Recommended Production Setup: Custom Endpoint Wiring
If you plan to use this in production, the recommended security practice is to keep your ZeroWeight API key on your server and expose your own backend endpoints to the frontend. That way, the browser never receives your private API key directly, while @zeroweight/react still gets the bundle and LiveKit token it needs.
<LiveKitAvatarSession
avatarId="your-avatar-id"
livekitUrl="wss://your-livekit-server.example.com"
sessionDuration={180}
inactivityTimeout={45000}
api={{
getBundle: (avatarId) =>
fetch(`/api/avatars/bundle/${avatarId}`).then((r) => r.json()),
getLiveKitToken: (avatarId, userName) =>
fetch(
`/api/livekit/token?avatar_id=${avatarId}&name=${userName}`,
).then((r) => r.json()),
}}
/>Build from Source
git clone <repo-url>
cd zeroweight-renderer-react
npm install
npm run buildOutput in dist/:
zeroweight-renderer-react.es.js— ES module (14 KB)zeroweight-renderer-react.cjs.js— CommonJS (11 KB)*.d.ts— TypeScript declarations
Deploy
npm publish --access publicLicense
MIT
