@voxera/sdk-react-native
v2.0.0
Published
React Native client for the Voxera realtime voice and video platform
Maintainers
Readme
@voxera/sdk-react-native
React Native bindings for the native Voxera iOS and Android SDKs. Media and signalling run in the native SDKs; this package is a typed bridge over them and deliberately does not ship a second JavaScript WebRTC implementation.
- Peer requirements: React
>=18.2.0, React Native>=0.76.0 - New architecture: the demo runs on Expo prebuild with RN 0.76
⚠️ The npm name is taken by an older, incompatible package
npm install @voxera/sdk-react-native resolves to version 1.0.1 on the
public registry, which is a different SDK with a different architecture: it
depends on @voxera/sdk-core and peer-depends on react-native-webrtc, i.e.
the JavaScript-WebRTC design this package replaced.
The version in this repository is 0.1.0 and is not published. Install it
from the repository, as demos/react-native does:
{
"dependencies": {
"@voxera/sdk-react-native": "file:../../pkg/react-native",
"@voxera/protocol": "file:../../pkg/protocol"
}
}@voxera/protocol must be linked explicitly and built first — this package
depends on it via file:, and its dist/ is gitignored.
Native artifacts
The bridge is useless without the native SDKs underneath it:
- iOS:
VoxeraSDKCocoaPod1.1.42(private repository) — thenpod install - Android:
com.voxera:voxera-sdk-android:1.1.42(private Maven repository)
Both resolve their WebRTC runtime transitively — WebRTC-lib 149.0.0 on iOS,
io.github.webrtc-sdk:android:144.7559.09 on Android. Do not add a second
WebRTC implementation to the host app. Two copies of the WebRTC symbols is a
duplicate-symbol link failure on iOS and a silent runtime conflict on Android.
Permissions
iOS needs NSMicrophoneUsageDescription and, for video,
NSCameraUsageDescription in Info.plist. Android merges its permissions from
the library manifest, but the app must still request them at runtime — which
useVoxera does for you on Android by default.
For Expo prebuild:
{ "expo": { "plugins": ["@voxera/sdk-react-native/app.plugin"] } }Quick start — the hook
useVoxera is the intended entry point. It owns the client lifecycle, merges
streamed message chunks into one entry per reply, and tracks the state a call
UI actually needs:
import { useVoxera } from "@voxera/sdk-react-native";
function CallScreen() {
const call = useVoxera();
const start = () =>
call.start({
appKey: "vx_pk_...",
serverUrl: "https://rtc.voxera-voice.com",
userId: "user-123",
});
return (
<>
<Text>{call.connectionState}</Text>
<Text>{call.transcript}</Text>
{call.messages.map((m) => (
<Text key={m.id}>{m.role}: {m.content}</Text>
))}
<Button title="Start" onPress={start} disabled={call.isBusy} />
<Button title="Mute" onPress={call.toggleMute} />
<Button title="Leave" onPress={call.leave} />
</>
);
}| Field | Notes |
| --- | --- |
| connectionState | idle · connecting · connected · reconnecting · disconnected · error |
| conversationState | idle · starting · active · ending · ended |
| speakingState | user · ai · none · searching |
| messages | Streamed chunks already merged — one entry per reply |
| transcript | Live speech-to-text |
| audioLevel / aiAudioLevel | For meters; these update many times a second |
| isMuted, isCameraOn, isActive, isBusy | UI state |
| error / clearError() | Last error as a string |
| client | The underlying VoxeraClient, or null. Escape hatch. |
Options: useVoxera({ autoStartConversation, requestMicrophonePermission }) —
both default to true. The second is a no-op on iOS, where the prompt is
driven by Info.plist.
audioLevel updates at audio rate. Depending on the whole call object in
a useCallback/useEffect dependency array re-subscribes many times a second.
Depend on the individual actions (call.start, call.leave) instead —
demos/react-native/App.tsx shows the pattern.
Direct client
When you need lifecycle control the hook does not give you:
import { VoxeraClient } from "@voxera/sdk-react-native";
const client = new VoxeraClient({ appKey, serverUrl, userId });
const sub = client.on("message", (m) => console.log(m.role, m.content));
await client.connect();
await client.startConversation();
await client.sendMessage("hello");
await client.setMuted(true);
await client.startCamera(true); // front camera
await client.switchCamera();
await client.stopCamera();
await client.endConversation();
await client.disconnect();
sub.remove();Events: connectionState, conversationState, speakingState, message,
transcript, error, audioLevel, aiAudioLevel, muteChanged,
localVideoTrack, remoteAudioTrack, remoteVideoTrack. Each on() returns
a subscription with .remove().
Limitations
- Publishable keys only.
VoxeraConfig.appKeyis required and there is nosessionTokenfield — unlike the web SDK, this bridge cannot use a backend-minted session token. Use avx_pk_*key restricted to specific agents in the manager, and never ship avx_sk_*key in an app binary. - One active client per bridge. A second concurrent client is not supported.
- Video tracks are exposed, rendering is not.
localVideoTrack/remoteVideoTrackemit track ids; the packaged native renderer component is not yet shipped. - Provider config fields (
ttsConfig,transcriptionConfig,selectedVoice,selectedModel) exist onVoxeraConfigbut are overwritten by the published agent version on a managed session. Configure the agent in the manager application instead.
Commands
npm run build
npm run typecheck
npm testdemos/react-native is a full working integration and the reference for
anything this document leaves ambiguous.
