Skip to content

SDKs

React Native SDK reference: hooks and errors

Every export of @voltkit-rtc/react-native: VoltkitProvider, hooks, VideoTrack and AudioTrack, audio routing, the call service, the Expo plugin and error codes.

The complete surface of @voltkit-rtc/react-native 0.1.0. For how to use it, start with the React Native SDK guide and its call screen; React Native with Expo covers the config plugin. The client underneath is @voltkit-rtc/client, whose methods, events and options are on the JavaScript SDK page.

Errors

Every failure is an RpcError from @voltkit-rtc/client, with the same codes as every VoltKit SDK.

Member
codeThe registry code: switch on it, never on the message
messageFor logs; it may be reworded between releases. It starts with the code
dataWhat the SDK or the server attached, typed unknown: read it after checking the code, as below

isTerminalError(code) and errorInfo(code) from @voltkit-rtc/client read the shared registry: whether a join refused with the code may be retried with the same token, and whether retrying later can help.

These are the codes the React Native SDK raises itself. Everything else, from the service and the client, is on Errors.

CodedataWhenWhat to do
media-unavailable{ permission, status }The camera ("camera") or microphone ("microphone") is not granted: publish() asked and the user refused, or a capture the SDK makes later (a reconnect, a switch), which only checks, found it missing. status is "denied" (the system may ask again) or "blocked" (only the app's settings can grant it). No device was openedExplain why the app needs it; for blocked, offer Linking.openSettings()
media-unavailable{ reason: "webrtc-missing" }react-native-webrtc's native module is not in the app: Expo Go, or an app not built again after installing it. The provider makes no client and reports it through onError; createClient throws it. The message says it needs a development buildBuild the app again; use a development build
media-unavailable{ reason: "native-missing", feature }The SDK's own native module is not in the app (Android): background calls, audio routing and native timers are off, and feature names what asked for themBuild the app again
media-unavailable{ reason }The camera or microphone could not open (another app holds it, or it failed): reason is the capture error's name, or capture-failedOffer audio only, or try again
media-unavailable{ feature: "callService" }The call service could not start: the manifest lacks the service or one of its permissions, or the app was in the background. Reported through onError; the call goes onAdd what the message names; start calls from a visible screen
media-unavailable{ platform: "ios", feature }An Android-only call on iOS: setSpeakerphoneOn, selectAudioOutput, switchDevice("audiooutput", ...)Leave the control out on iOS
media-unavailableswitchCamera() or switchDevice("videoinput", ...) with no camera published, or startRecording(); also a failure inside the audio router on Android (selectAudioOutput, useAudioOutput().select, switchDevice("audiooutput", ...) or switchDevice("audioinput", ...), and the call audio the provider holds, which reaches onError), with the native side's words in the messagePublish a camera first; recording needs a browser; for the router, try again or leave the output as it is
camera-failedA camera switch failed; the old camera stays on airTry again
bad-paramsAn id no camera, microphone or output has, or an output chosen in media audio modeUse an id from useDevices() or useAudioOutput()
screen-share-service-requiredstartScreenShare() in a build without the screen-capture serviceTurn on the plugin's android.screenShare, or add the meta-data, and build again
screen-share-deniedThe user cancelled Android's screen-capture consentNothing; offer the button again
screen-share-unsupportedstartScreenShare() on iOSLeave the control out on iOS
screen-share-activeA share is already running or startingWait for it, or stop it first
screen-share-failedThe screen capture could not start; the message says whyTry again
music-unavailablepublishMusic(): in-app music needs a browserLeave it out
not-connectedA useLocalMedia() call with no connected clientWait for connected
import { RpcError } from "@voltkit-rtc/client";

type MediaProblem = {
  permission?: "camera" | "microphone";
  status?: "denied" | "blocked";
  reason?: string;
  feature?: string;
  platform?: string;
};

/** Words for a media-unavailable error, or null for any other error. */
export function mediaProblem(err: unknown): string | null {
  if (!(err instanceof RpcError) || err.code !== "media-unavailable") return null;
  const data = (err.data ?? {}) as MediaProblem;
  if (data.permission) return data.status === "blocked" ? `Allow the ${data.permission} in Settings.` : `The ${data.permission} is needed.`;
  if (data.reason === "webrtc-missing") return "This build cannot make calls. Install the latest version of the app.";
  if (data.feature === "callService") return "Calls may stop when you leave the app.";
  return "The camera or microphone is not available.";
}

API reference

VoltkitProvider

<VoltkitProvider> holds one StreamClient for the components below it: it makes it with the options of reactNativeClientOptions, connects it, holds the phone's call audio and the call service while it is connected, and closes it on unmount. It is the React SDK's provider underneath, so every re-exported hook works below it.

PropTypeDefault
wsUrlstringThe room's WebSocket URL, from your backend
tokenstringA room token from your backend. A new one for the same session goes to updateToken; one for another room or identity makes a new client
connectbooleantrueConnect the client when it is idle
onConnected(result: JoinResult) => voidAfter every successful join, reconnects included
onError(error: Error) => voidEvery error event, a failed connect, a refused token, a call service that could not start, and missing native code
onClosed(reason: ClosedReason) => voidThe session ended for good
clientStreamClient | nullA client you made with createClient and own; never closed by the provider
clientOptionsClientOptionsLaid over React Native's own options when the client is made
audioAudioSessionOptions | falsefalse leaves the audio mode, focus and routing to your app
audio.speakerphonebooleantrueThe loudspeaker (true) or the earpiece (false) when no headset is connected, unless setSpeakerphoneOn chose
audio.managebooleantruefalse is the same as audio={false}
background.pauseCamerabooleantrueSwitch the camera off while the app is in the background, and back on when it returns
background.pauseMicbooleanfalseThe same for the microphone; leave it off for calls
callServiceboolean | CallServiceTexttrueStart the call service when the call connects, if the manifest declares it. CallServiceText sets the notification: title (default "Call in progress"), text ("Tap to return to the call") and leaveLabel ("Leave")
childrenReactNode

The client it makes has the options reactNativeClientOptions({ background, clientOptions }) returns. Where react-native-webrtc's native code is missing, it makes no client: onError gets media-unavailable (webrtc-missing) and the hooks keep their defaults.

Hooks from the React SDK

Re-exported unchanged from @voltkit-rtc/react 0.1.1; the React SDK's hooks describe each.

HookReturns
useVoltkitClient()The provider's StreamClient, or null
useRoom()RoomState: client, info, state, error, connect(), leave()
useConnectionState()ConnectionState
useParticipants()ParticipantInfo[]
useRemoteTracks(filter?)RemoteTrack[]; the filter takes identity, kind and source
useNetworkQuality()NetworkQuality | null
useWalletLow()WalletLowState: active, remainingSeconds, notice
useVoltkitEvent(event, handler)Nothing: calls handler for each event while mounted
useAudioLevels()AudioLevel[], every 500 ms on React Native
useActiveSpeakers()Who is speaking, most active first
useIsSpeaking(identity)boolean
useStreamFallback(producerId)"bandwidth", "audio-protection" or null
usePublishFallback()boolean
useMediaWarnings()The camera and microphone warnings in force, such as mic-silent and mic-interrupted
useCpuConstrained()boolean
useDevices(kind?)MediaDevice[]: cameras with facing, microphones, and on Android the outputs with type

useLocalMedia

useLocalMedia(): LocalMedia: the React SDK's useLocalMedia, with two additions.

State
streamYour camera and microphone as published (a MediaStreamLike: react-native-webrtc's stream, by shape), or null
publishingAnything is published
micEnabled, camEnabledSwitched on
audioQuality, videoQuality, videoFrameRateWhat is sent, after the project's limits
screenSharing, screenStreamThe screen being shared
permissionsWhat the project lets this client publish
facingMode"user" (front) or "environment" (back): the camera in use; null without a camera
Call
publish(options?)Ask for the permissions, then open the camera and microphone and publish them. Options: the client's publish options, audio, video and facingMode
unpublish()Stop publishing everything and release the devices
setMicEnabled(on), setCamEnabled(on)Switch one off or on
setAudioQuality(quality), setVideoQuality(quality), setVideoFrameRate(frameRate)Change what is sent while live; each resolves to what is used
switchCamera(): Promise<boolean>Front and back on the same track and producer; resolves true when the front camera is now in use. media-unavailable before a camera is published
startScreenShare(options?), stopScreenShare()Android screen sharing, after the system's consent

VideoTrack

<VideoTrack> shows one video track in react-native-webrtc's RTCView.

PropTypeDefault
trackMediaStreamTrackLike | null | undefinedRequired. The video: a remote track's track, or useLocalMedia().stream?.getVideoTracks()[0]; nothing while null. Typed by shape (react-native-webrtc's track; a remote track's track fits), so no DOM library is needed
producerIdstringA remote video's id: adaptive video from this view's size and visibility. Leave it out for your own camera
mirrorbooleanfalseFlip horizontally
objectFit"cover" | "contain""cover"The view filled and cropped, or the whole picture with bars
zOrdernumberWhich overlapping video view is drawn on top: 0 for a full-screen video, 1 for a preview over it
visiblebooleantruefalse pauses a remote video at the server while the view cannot be seen
onDimensionsChange(size: { width: number; height: number }) => voidThe first frame arrived, or the size changed
styleStyleProp<ViewStyle>
testIDstring

One native stream per track, shared by its views and released before the connection it came on closes.

AudioTrack

<AudioTrack> renders nothing: remote sound plays without it.

PropTypeDefault
trackMediaStreamTrackLike | null | undefinedRequired. A remote audio track; nothing while null
volumenumber0 to 1, clamped, this track only: 1 is the track as received, and unset leaves it as it is
mutedbooleanfalseSilence this track on this phone; restored when false or on unmount

useAudioOutput, setSpeakerphoneOn and selectAudioOutput

useAudioOutput(): AudioOutput returns where call audio plays and the calls that change it. Android only.

Field
supportedOutputs can be chosen: Android with the SDK's native module, in the communication audio mode. False on iOS and in the media mode
outputsMediaDevice[]: the loudspeaker, the earpiece, and the headsets connected now, each with deviceId, label and type (speaker, earpiece, wiredHeadset, bluetooth, usb, other)
currentThe output in use, or null while no client holds the audio
speakerphoneThe loudspeaker is in use, or, while no call holds the audio, preferred
select(deviceId): Promise<void>Play call audio on that output while it stays connected, even over a headset. bad-params for an output that is not there, and in the media mode
setSpeakerphoneOn(on): Promise<void>The loudspeaker or the earpiece when no headset is connected; a headset still wins

setSpeakerphoneOn(on): Promise<void> and selectAudioOutput(deviceId): Promise<void> are the same calls as functions. They work before any client exists, and the choice holds for the calls joined later. They reject with media-unavailable on iOS (data.platform "ios") and where the SDK's native module is missing.

useCallService

useCallService() returns the call service's state, for a settings screen or a warning.

Field
declaredThe app's manifest declares VoltkitCallService
runningIt runs now
typesThe foreground service types it runs with: microphone, mediaPlayback, camera, mediaProjection; empty when stopped

The provider starts it when a call connects, if declared and callService is not false, and stops it when the call ends. Its notification has a Leave button, which ends the call, and swiping the app away ends the call too.

requestMediaPermissions, checkMediaPermissions and usePermissions

PermissionKind is "camera", "microphone" or "notifications" (the call's notification, Android 13 and later), and PermissionStatus is "granted", "denied" (the dialog may still show) or "blocked" (only the app's settings can grant it).

requestMediaPermissions(kinds): Promise<PermissionStatuses>Show the system's dialogs for those not granted yet, and resolve with each one's status. Never rejects
checkMediaPermissions(kinds): Promise<PermissionStatuses>Each one's status now, without asking. Never rejects
usePermissions(kinds): Permissionsstatuses (null until first checked, and checked again whenever the app comes back to the front), granted (every one granted), request() and refresh()

publish() asks for the camera and microphone by itself; these are for asking earlier, on a screen before the call. The SDK's own captures afterwards, such as a reconnect, only check and never ask: a dialog must not open in the background. On Android they use PermissionsAndroid; on iOS, react-native-webrtc's permission requests, where a refusal is blocked from then on. Below Android 13 there is no notification permission to ask for.

createClient and reactNativeClientOptions

createClient(wsUrl: string, token: string, options?: CreateClientOptions): StreamClient makes a StreamClient for React Native, not yet connected. CreateClientOptions is { background?: BackgroundOptions; clientOptions?: ClientOptions }, where BackgroundOptions is { pauseCamera?: boolean; pauseMic?: boolean } (defaults true and false). Pass the client to the provider's client prop, or call connect() yourself, and close() it when you are done: the provider never closes a client it was given. It throws media-unavailable (webrtc-missing) where react-native-webrtc's native module is missing. Never make one with new StreamClient(...) alone, which would look for a browser.

reactNativeClientOptions(options?: CreateClientOptions): ClientOptions returns the options such a client is made with: handlerName: "ReactNative106", platform: reactNativePlatform(), pauseCameraInBackground and pauseMicInBackground from background, and then your clientOptions, which win.

reactNativePlatform

reactNativePlatform(): ClientPlatform is the platform the client runs on in React Native (ClientOptions.platform of @voltkit-rtc/client 0.9.12): capture, devices and audio routing, streams and their release order, the app's visibility from the process lifecycle, the network, and native timers. One object per process, shared by every client. createClient and the provider pass it for you.

preflight

preflight(wsUrl: string, token: string, options?: ReactNativePreflightOptions): Promise<PreflightReport> is the pre-call check: it joins with a client of its own, publishes for a few seconds (5 s unless durationMs says otherwise) and leaves. React Native cannot make a test tone or picture, so without media it publishes the microphone when its permission is already granted, and otherwise tests the join alone; it never asks for a permission. The options are the client's PreflightOptions (media, durationMs, clientOptions) and background. It never rejects: read ok, then steps, path, rttMs, lossPct, uploadKbps, level and warnings. Give it a token for a test room of its own.

registerVoltkitGlobals

registerVoltkitGlobals(): boolean registers react-native-webrtc's globals, which mediasoup-client needs. Importing the package calls it, and calling it again does nothing. It returns false, without throwing, where react-native-webrtc's native module is missing.

SDK_VERSION

SDK_VERSION is this package's version, "0.1.0": what getDiagnostics() reports, beside react-native-webrtc's version and the Android version. When the app's native code was built from another version, the SDK warns in the log to build the app again.

Types

One import covers a component. This package's own: VoltkitProviderProps, AudioSessionOptions, CallServiceText, CallServiceState, BackgroundOptions, CreateClientOptions, ReactNativePreflightOptions, LocalMedia, LocalPublishOptions, VideoTrackProps, AudioTrackProps, MediaStreamLike, MediaStreamTrackLike, MediaTrackSettingsLike (react-native-webrtc's stream, track and track settings by shape: what useLocalMedia().stream and the track props are, with no DOM library needed), AudioOutput, PermissionKind, PermissionStatus, PermissionStatuses and Permissions. The React SDK's: RoomState, RemoteTrackFilter and WalletLowState. The client's: StreamClient, ClientOptions, ClientPlatform, ClosedReason, ConnectionState, ErrorCode, JoinResult, MediaDevice, AudioDeviceType, CameraFacing, DeviceKind, ParticipantInfo, PublishOptions, PreflightReport, RemoteTrack, Role, ChatMessage and the rest. They are types only: import values such as RpcError, ChatClient and isTerminalError from @voltkit-rtc/client.

The Expo config plugin

"@voltkit-rtc/react-native" in app.json's plugins, with the options cameraPermission, microphonePermission, android.camera, android.callService, android.callServiceCamera, android.screenShare, android.audioMode, android.hardwareEchoCancellation, ios.backgroundAudio and ios.voip. Defaults and what each writes: Configure the plugin.

Android manifest

What an app without Expo writes by hand, all in android/app/src/main/AndroidManifest.xml:

Entry
RECORD_AUDIO, CAMERAThe microphone, and the camera if you publish video
<uses-feature android:name="android.hardware.microphone" android:required="false" />, and the same for android.hardware.camera, android.hardware.camera.autofocus and android.hardware.camera.front with CAMERAGoogle Play reads those permissions as hardware the app cannot do without and hides it from devices without them, unless the manifest marks them as not required
FOREGROUND_SERVICE, FOREGROUND_SERVICE_MICROPHONE, FOREGROUND_SERVICE_MEDIA_PLAYBACK, POST_NOTIFICATIONSThe call service
<service android:name="com.voltkit.rtc.reactnative.VoltkitCallService" android:exported="false" android:foregroundServiceType="microphone|mediaPlayback" />The call service; add camera or mediaProjection to the types, with their permissions, to keep the camera in the background or share the screen
in.voltkit.rtc.AUDIO_MODEMeta-data: communication (the default) or media
in.voltkit.rtc.HW_AECMeta-data: the phone's own echo canceller, true (the default) or false
in.voltkit.rtc.SCREEN_SHAREMeta-data: react-native-webrtc's screen-capture service, false (the default) or true, with FOREGROUND_SERVICE_MEDIA_PROJECTION

INTERNET, ACCESS_NETWORK_STATE, MODIFY_AUDIO_SETTINGS and the startup provider come with the SDK's own manifest. The full blocks are in the guide: Calls in the background and Audio routing.

Last updated . Something unclear or wrong? Tell us.