# Pre-call self-test

## Introduction

Build a pre-call self-test screen where users record a short loopback of their own microphone, camera, and network path before joining a real call. Once recorded, they can watch the result back and optionally share the local video file.

This recipe uses the [loopback recording API](/video/docs/react-native/advanced/loopback-recording/). The result is a two-screen flow: a recording screen and a playback / share screen.

## Implementation

<admonition type="info">

Consider using a dedicated [call type](/video/docs/react-native/guides/configuring-call-types/) (for example `pre-call-test`) and a recognisable call ID (for example prefixed with `test_recording_`) for the loopback call. That keeps self-tests cleanly separated from real call sessions in logs, analytics, and the Stream dashboard, and lets you scope permissions and defaults independently.

</admonition>

### Step 1: Build the recording UI

Inside the recording screen, grab the active call with `useCall()` and drive the UI from `useLoopbackRecording`, using `recordingState` and the call's connecting state to reflect progress through join and record. When `startRecording()` resolves with a URI, you have a local video file you can play back or share via the OS share sheet; see the next step.

You can also render the live `loopbackVideoStream` via `RTCView` so the user sees the SFU echo immediately.

```tsx
import React, { useCallback, useMemo } from "react";
import { Button, View } from "react-native";
import { RTCView } from "@stream-io/react-native-webrtc";
import {
  CallingState,
  useCall,
  useCallStateHooks,
  useLoopbackRecording,
} from "@stream-io/video-react-native-sdk";

const PreCallTest = () => {
  const call = useCall();
  const { useCallCallingState } = useCallStateHooks();
  const callingState = useCallCallingState();
  const { startRecording, stopRecording, recordingState, loopbackVideoStream } =
    useLoopbackRecording();

  const onStart = useCallback(async () => {
    if (!call) return;
    await call.join({ create: true, allowOwnTracksLoopback: true });
    const uri = await startRecording();
    call.leave();
    if (uri) {
      // `uri` is a `file://` URI of a local video on the device.
      // Use it to play the recording back or to share it via the OS share sheet.
    }
  }, [call, startRecording]);

  const isConnecting =
    (callingState === CallingState.JOINING ||
      callingState === CallingState.JOINED) &&
    recordingState !== "recording";

  const label = useMemo(() => {
    if (recordingState === "recording") return "Stop recording";
    if (isConnecting) return "Connecting…";
    return "Start recording";
  }, [recordingState, isConnecting]);

  const onPress =
    recordingState === "recording" ? () => stopRecording() : onStart;

  return (
    <View style={{ flex: 1 }}>
      {loopbackVideoStream ? (
        <RTCView
          streamURL={loopbackVideoStream.toURL()}
          objectFit="cover"
          style={{ flex: 1 }}
        />
      ) : null}
      <Button title={label} onPress={onPress} disabled={isConnecting} />
    </View>
  );
};
```

### Step 2: Build the playback / share screen

The results screen takes the file URI as a prop and plays it back with `react-native-video`. Add a Share button to let users export the file via `react-native-share`.

```tsx
import React, { useRef, useState } from "react";
import { Button, Pressable, StyleSheet, View } from "react-native";
import Video, { VideoRef } from "react-native-video";
import Share from "react-native-share";

export const TestRecordingResults = ({ uri }: { uri: string }) => {
  const videoRef = useRef<VideoRef>(null);
  const [paused, setPaused] = useState(true);

  const handleShare = () => {
    Share.open({
      url: uri,
      type: "video/mp4",
      failOnCancel: false,
    }).catch(() => {});
  };

  return (
    <View style={{ flex: 1 }}>
      <View style={{ flex: 1 }}>
        <Video
          ref={videoRef}
          source={{ uri }}
          paused={paused}
          style={StyleSheet.absoluteFillObject}
          resizeMode="cover"
        />
        <Pressable
          onPress={() => setPaused((p) => !p)}
          style={StyleSheet.absoluteFillObject}
        />
      </View>
      <Button title="Share" onPress={handleShare} />
    </View>
  );
};
```

## Surfacing stats during the test

`useCallStatsReport()` works inside the loopback call too. Render a small overlay of latency, jitter, and bitrate while the user is recording so connectivity issues surface immediately. See the [Call Stats Report](/video/docs/react-native/advanced/stats/) guide.


---

This page was last updated at 2026-06-26T15:47:35.207Z.

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react-native/ui-cookbook/pre-call-self-test/](https://getstream.io/video/docs/react-native/ui-cookbook/pre-call-self-test/).