import { useLoopbackRecording } from "@stream-io/video-react-native-sdk";
const {
startRecording,
stopRecording,
clearRecordings,
getRecordings,
recordingState,
loopbackVideoStream,
loopbackAudioStream,
} = useLoopbackRecording();Pre-call test recording
The loopback recording API lets you record a short self-test of the local participant's media as it travels through Stream's SFU. The local video file is written to the device — nothing is uploaded.
This is different from server-side recording, which records the full call on Stream's infrastructure. Use the loopback API for local, pre-call diagnostics.
When to use it
- Pre-call device check — verify the user's microphone, camera, and network path end-to-end before they join a real call.
- Diagnostics builds — capture a short reference clip when users report audio or video quality issues, so you have a reproducible artifact to inspect.
How it works
The hook records your published media after it has made a full round trip through Stream's servers — capturing what other participants would actually see and hear, not just your local camera and microphone. Any problem along that path shows up in the recording.
Recordings are written to a Stream-managed directory inside your app's private storage (iOS temporary storage, Android cache). Files stay there until you remove them — clearing the directory is your responsibility, and the API exposes clearRecordings() for that.
Requirements
The component must be rendered inside <StreamCall>, and the call must be joined with allowOwnTracksLoopback: true.
No other participants can be present. The hook will not start recording when the call is shared.
API reference
| Field | Description |
|---|---|
startRecording(options?) | Starts a recording. Returns a promise that resolves with the file:// URI of the produced video file once recording finishes, or null if no file was produced (for example, you stopped the recording before any data arrived). Pass { includeVideo: false } for an audio-only recording, or { maxDurationMs } to override the default 10-second cap (clamped to a 5-second minimum and a 2-minute maximum). |
stopRecording() | Stops the recording early. During 'awaiting-streams' it aborts the pending wait; during 'recording' it finalises the file. |
clearRecordings() | Recursively deletes every file under the SDK's local recordings directory. |
getRecordings() | Returns every file:// URI in that directory, sorted most-recent first. |
recordingState | One of 'idle', 'awaiting-streams', or 'recording'. |
loopbackVideoStream | The video MediaStream echoed back by the SFU, when present. Render it with RTCView to give the user live feedback while recording. |
loopbackAudioStream | The audio MediaStream echoed back by the SFU, when present. |
Minimal example
import React, { useCallback, useEffect, useMemo } from "react";
import { Button, View } from "react-native";
import {
Call,
StreamCall,
useLoopbackRecording,
useStreamVideoClient,
} from "@stream-io/video-react-native-sdk";
export function PreCallTest() {
const client = useStreamVideoClient();
const call = useMemo<Call | undefined>(() => {
if (!client) return undefined;
return client.call("default", "self-test-" + Date.now());
}, [client]);
useEffect(() => {
if (!call) return;
call.getOrCreate();
return () => {
call.leave();
};
}, [call]);
if (!call) return null;
return (
<StreamCall call={call}>
<RecordButton call={call} />
</StreamCall>
);
}
function RecordButton({ call }: { call: Call }) {
const { startRecording, recordingState } = useLoopbackRecording();
const onPress = useCallback(async () => {
await call.join({ create: true, allowOwnTracksLoopback: true });
const uri = await startRecording();
call.leave();
if (uri) {
// You now have a local video file on disk. Play it back or share it.
console.log("Recording saved at", uri);
}
}, [call, startRecording]);
return (
<View>
<Button title={recordingState} onPress={onPress} />
</View>
);
}Lifecycle and limits
- Recordings auto-stop after 10 seconds by default. Override via
startRecording({ maxDurationMs })— values outside the[5 seconds, 2 minutes]range snap to the nearest bound. startRecording()waits up to 10 seconds for the SFU to echo the loopback tracks back. If the wait times out the promise rejects.- The recording auto-stops and finalises if the call leaves or the screen unmounts mid-recording — the file is still produced.
See also
- Pre-call self-test cookbook — a complete two-screen UI recipe (record → play back → share).
- Server-side recording — cloud-hosted recording of the full call.
- Call Stats Report — surface latency, jitter, and bitrate during the loopback test for richer diagnostics.