# Quickstart (/en/realtime-media/rtc/get-started-sdk/react-native)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

This page provides a step-by-step guide on how to create a basic real-time app using the Agora RTC SDK. The same steps apply whether you're building voice calling, video calling, interactive live streaming, or broadcast streaming — only a few configuration values change based on your use case.

## Understand the tech

To start a real-time session, implement the following steps in your app:

* **Initialize the engine**: Before calling other APIs, create and initialize an engine instance.

* **Join a channel**: Call methods to create and join a channel.

  * **Join as a host**: A live streaming event has one or more hosts. A host publishes audio and video to the channel and can also subscribe to streams from other hosts.

  * **Join as audience**: Audience members can only subscribe to streams published by hosts.

  <CalloutContainer type="info">
    <CalloutTitle>
      Note
    </CalloutTitle>

    <CalloutDescription>
      For the voice calling and video calling use cases, each user joins as a host.
    </CalloutDescription>
  </CalloutContainer>

* **Send and receive audio and video**: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.

* For streaming applications, set the latency level based on your use case:

  * **Interactive live streaming**: Optimized for real-time interaction with ultra-low latency. Use this when hosts and audience need to interact quickly, such as in live Q\&A sessions, interactive classrooms, or auctions.

  * **Broadcast streaming**: Optimized for scalability with slightly higher latency. Use this for large-scale events where one-way broadcasting is sufficient, such as concerts, sports events, or news broadcasts.

![Realtime communication workflow](https://assets-docs.agora.io/images/video-sdk/video-call.svg)

## Prerequisites

* A camera and a microphone.
* A valid Agora account and project. See [Agora account management](/en/realtime-media/rtc/manage-agora-account) for details.
* React Native 0.60 or later. See [Get Started with React Native](https://reactnative.dev/docs/environment-setup).
* Node.js 16 or later.
* Depending on your target platform:
  * Android: a machine running macOS, Windows, or Linux; [JDK 11](https://openjdk.org/projects/jdk/11/) or later; the latest version of Android Studio; and a physical or virtual Android device running Android 5.0 or later.
  * iOS: a machine running macOS; Xcode 10 or later; CocoaPods; and a physical or virtual iOS device running iOS 9.0 or later. If you use React Native 0.63 or later, ensure your iOS version is 10.0 or later.

## Set up your project

### Create a project

To create a new application using [React Native Community CLI](https://github.com/react-native-community/cli), see [Get Started Without a Framework](https://reactnative.dev/docs/getting-started-without-a-framework).

### Install the SDK

Download and add the Agora RTC SDK to your React Native project. Choose either of the following methods:

<Tabs defaultValue="npm">
  <TabsList>
    <TabsTrigger value="npm">
      npm
    </TabsTrigger>

    <TabsTrigger value="yarn">
      Yarn
    </TabsTrigger>
  </TabsList>

  <TabsContent value="npm">
    In your project folder, execute the following command:

    ```bash
    npm i --save react-native-agora
    ```
  </TabsContent>

  <TabsContent value="yarn">
    In your project folder, execute the following commands:

    ```bash
    # Install yarn
    npm install -g yarn

    # Use yarn to download Agora React Native SDK
    yarn add react-native-agora
    ```
  </TabsContent>
</Tabs>

<CalloutContainer type="info">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    React Native 0.60.0 and later support automatic linking of native modules. Manual linking is not recommended. See [Autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md) for details
  </CalloutDescription>
</CalloutContainer>

## Implement Realtime Communication

This section guides you through the implementation of basic real-time audio and video interaction in your app.

The following figure illustrates the essential steps:

<Accordions>
  <Accordion title="Quick start sequence">
    ![Realtime Communication quick start sequence](https://assets-docs.agora.io/images/video-sdk/quick-start-sequence.svg)
  </Accordion>
</Accordions>

This guide includes [complete sample code](#complete-sample-code) that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps and use the code in your `App.tsx` file.

### Import dependencies

Add the required Agora dependencies to your app.

```tsx
import {
    createAgoraRtcEngine,
    ChannelProfileType,
    ClientRoleType,
    IRtcEngine,
    RtcSurfaceView,
    RtcConnection,
    IRtcEngineEventHandler,
} from 'react-native-agora';
```

### Initialize the engine

For real-time communication, call `createAgoraRtcEngine` to create an `RtcEngine` instance, and then `initialize` the engine using `RtcEngineContext` to specify the [App ID](/en/realtime-media/rtc/manage-agora-account#get-the-app-id).

```tsx
const appId = '<-- Insert app ID -->';

const setupVideoSDKEngine = async () => {
    if (Platform.OS === 'android') { await getPermission(); }
    agoraEngineRef.current = createAgoraRtcEngine();
    const agoraEngine = agoraEngineRef.current;
    await agoraEngine.initialize({ appId: appId });
};
```

### Join a channel

To join a channel, call `joinChannel` with the following parameters:

* **Channel name**: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.

* **Authentication token**: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a [token server](/en/realtime-media/rtc/build/authenticate-users/deploy-token-server) in your security infrastructure. For the purpose of this guide [Generate a temporary token](/en/realtime-media/rtc/manage-agora-account#generate-temporary-tokens).

* **User ID**: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to `0` when joining a channel, the SDK generates a random number for the user ID and returns the value in the `onJoinChannelSuccess` callback.

* **Channel media options**: Configure `ChannelMediaOptions` to define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.

Set the `channelProfile`, `clientRoleType`, and `audienceLatencyLevel` according to your use case:

| Use case                   | Channel profile                  | Client role                                     | Latency level                                   |
| -------------------------- | -------------------------------- | ----------------------------------------------- | ----------------------------------------------- |
| Voice calling              | `ChannelProfileCommunication`    | `ClientRoleBroadcaster`                         | N/A                                             |
| Video calling              | `ChannelProfileCommunication`    | `ClientRoleBroadcaster`                         | N/A                                             |
| Interactive live streaming | `ChannelProfileLiveBroadcasting` | `ClientRoleBroadcaster` or `ClientRoleAudience` | `AudienceLatencyLevelUltraLowLatency` (default) |
| Broadcast streaming        | `ChannelProfileLiveBroadcasting` | `ClientRoleBroadcaster` or `ClientRoleAudience` | `AudienceLatencyLevelLowLatency`                |

<CalloutContainer type="info">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    * Only broadcasters can publish media. Audience members cannot publish until promoted to broadcaster.
    * Choose Communication mode for calls where every user publishes, typically fewer than 17 participants. Choose Live Broadcasting mode for larger events with separate hosts and audience.
    * The latency level only applies to the audience role, and affects your [pricing](/en/realtime-media/rtc/reference/pricing#subscription-packages) tier.
  </CalloutDescription>
</CalloutContainer>

The following example shows the configuration for interactive live streaming with separate host and audience roles:

```tsx
const token = '<-- Insert token -->';
const channelName = '<-- Insert channel name -->';
const localUid = 0; // Local user UID, no need to modify

// Define the join method called after clicking the join channel button
const join = async () => {
    if (isJoined) {
        return;
    }
    if (isHost) {
        // Join the channel as a broadcaster
        agoraEngineRef.current?.joinChannel(token, channelName, localUid, {
            // Set the channel profile according to your use case (see table above)
            channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
            // Set user role to broadcaster
            clientRoleType: ClientRoleType.ClientRoleBroadcaster,
            // Publish audio collected by the microphone
            publishMicrophoneTrack: true,
            // Publish video collected by the camera
            publishCameraTrack: true,
            // Automatically subscribe to all audio streams
            autoSubscribeAudio: true,
            // Automatically subscribe to all video streams
            autoSubscribeVideo: true,
        });
    } else {
        // Join the channel as an audience
        agoraEngineRef.current?.joinChannel(token, channelName, localUid, {
            // Set the channel profile according to your use case (see table above)
            channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
            // Set user role to audience
            clientRoleType: ClientRoleType.ClientRoleAudience,
            // Only takes effect for the audience role
            audienceLatencyLevel: AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
            // Do not publish audio collected by the microphone
            publishMicrophoneTrack: false,
            // Do not publish video collected by the camera
            publishCameraTrack: false,
            // Automatically subscribe to all audio streams
            autoSubscribeAudio: true,
            // Automatically subscribe to all video streams
            autoSubscribeVideo: true,
        });
    }
};
```

<CalloutContainer type="info">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    Since the engine runs locally, reloading the app using the Metro bundler may cause the engine reference to be lost, preventing channel joining. To resolve this issue, restart the app.
  </CalloutDescription>
</CalloutContainer>

### Subscribe to RTC SDK events

The RTC SDK provides an interface for subscribing to channel events. To use it, create an instance of `IRtcEngineEventHandler` and implement the event methods you want to handle. Call `registerEventHandler` to register your custom event handler.

```tsx
const [remoteUid, setRsemoteUid] = useState(0); // Uid of the remote user

const setupEventHandler = () => {
    eventHandler.current = {
        // Triggered when the local user successfully joins a channel
        onJoinChannelSuccess: () => {
            setMessage('Successfully joined channel: ' + channelName);
            setupLocalVideo();
            setIsJoined(true);
        },
        // Triggered when a remote user joins the channel
        onUserJoined: (_connection: RtcConnection, uid: number) => {
            setMessage('Remote user ' + uid + ' joined');
            setRemoteUid(uid);
        },
        // Triggered when a remote user leaves the channel
        onUserOffline: (_connection: RtcConnection, uid: number) => {
            setMessage('Remote user ' + uid + ' left the channel');
            setRemoteUid(uid);
        },
    };
    // Register the event handler
    agoraEngineRef.current?.registerEventHandler(eventHandler.current);
};
```

<CalloutContainer type="info">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    To ensure that you receive all RTC SDK events, register the event handler before joining a channel.
  </CalloutDescription>
</CalloutContainer>

### Enable the video module

Call `enableVideo` to activate the video module, and use `startPreview` to enable the local video preview.

```tsx
const setupLocalVideo = () => {
    agoraEngineRef.current?.enableVideo();
    agoraEngineRef.current?.startPreview();
};
```

### Display the local video

To display the local user's video stream, use the `RtcSurfaceView` component. Set the `uid` to `0` and `style` the video container.

```tsx
<React.Fragment key={0}>
    <RtcSurfaceView canvas={{ uid: 0 }} style={ width: '90%', height: 200 } />
</React.Fragment>
```

### Display remote video

To display the remote user's video, use the `RtcSurfaceView` component. Pass the `remoteUid` of the remote user to the component and set the desired `style` for the video container.

```tsx
<React.Fragment key={remoteUid}>
    <RtcSurfaceView
        canvas={{ uid: remoteUid }}
        style={ width: '90%', height: 200 }
    />
</React.Fragment>
```

### Handle permissions

To access the camera and microphone, ensure that the user grants the necessary permissions when the app starts.

<Tabs defaultValue="android">
  <TabsList>
    <TabsTrigger value="android">
      Android
    </TabsTrigger>

    <TabsTrigger value="ios">
      iOS
    </TabsTrigger>
  </TabsList>

  <TabsContent value="android">
    On Android devices, pop up a prompt box to obtain permission to use the microphone and camera.

    ```tsx
    // Import components related to Android device permissions

    const getPermission = async () => {
        if (Platform.OS === 'android') {
            await PermissionsAndroid.requestMultiple([
                PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
                PermissionsAndroid.PERMISSIONS.CAMERA,
            ]);
        }
    };
    ```
  </TabsContent>

  <TabsContent value="ios">
    In Xcode, open the `info.plist` file and add the following content to the list on the right to obtain the corresponding device permissions:

    | Key                                    | Type   | Value                                                                                       |
    | -------------------------------------- | ------ | ------------------------------------------------------------------------------------------- |
    | Privacy - Microphone Usage Description | String | The purpose of using the microphone, for example, for a call or live interactive streaming. |
    | Privacy - Camera Usage Description     | String | The purpose of using the camera, for example, for a call or live interactive streaming.     |
  </TabsContent>
</Tabs>

### Leave the channel

To exit a Realtime Communication channel, call `leaveChannel`.

```tsx
const leave = () => {
    // Leave the channel
    agoraEngineRef.current?.leaveChannel();
    setIsJoined(false);
};
```

### Clean up resources

Before closing your client, unregister the event handler and call `release` to free up resources.

```tsx
const cleanupAgoraEngine = () => {
    return () => {
        agoraEngineRef.current?.unregisterEventHandler(eventHandler.current!);
        agoraEngineRef.current?.release();
    };
};
```

<CalloutContainer type="info">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    After calling `release,` methods and callbacks of the SDK are no longer available. To use the SDK functions again, create a new engine instance.
  </CalloutDescription>
</CalloutContainer>

### Complete sample code

A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To use the sample code, copy the code into `ProjectName/App.tsx` to quickly implement the basic functionality.

<Accordions>
  <Accordion title="Complete sample code for Realtime Communication">
    ```tsx
    // Import React Hooks
    import React, { useRef, useState, useEffect } from 'react';

    // Import user interface elements
    import {
        SafeAreaView,
        ScrollView,
        StyleSheet,
        Text,
        View,
        Switch,
    } from 'react-native';

    // Import components related to obtaining Android device permissions
    import { PermissionsAndroid, Platform } from 'react-native';

    // Import Agora SDK
    import {
        createAgoraRtcEngine,
        ChannelProfileType,
        ClientRoleType,
        IRtcEngine,
        RtcSurfaceView,
        RtcConnection,
        IRtcEngineEventHandler,
        VideoSourceType,
    } from 'react-native-agora';

    // Define basic information
    const appId = '<-- Insert App ID -->';
    const token = '<-- Insert Token -->';
    const channelName = '<-- Insert Channel Name -->';
    const localUid = 0; // Local user Uid, no need to modify

    const App = () => {
        const agoraEngineRef = useRef<IRtcEngine>(); // IRtcEngine instance
        const [isJoined, setIsJoined] = useState(false); // Whether the local user has joined the channel
        const [isHost, setIsHost] = useState(true); // User role
        const [remoteUid, setRemoteUid] = useState(0); // Uid of the remote user
        const [message, setMessage] = useState(''); // User prompt message
        const eventHandler = useRef<IRtcEngineEventHandler>(); // Implement callback functions

        useEffect(() => {
            const init = async () => {
                await setupVideoSDKEngine();
                setupEventHandler();
            };
            init();
            return () => {
                cleanupAgoraEngine(); // Ensure this is synchronous
            };
        }, []); // Empty dependency array ensures it runs only once

        const setupEventHandler = () => {
            eventHandler.current = {
                onJoinChannelSuccess: () => {
                    setMessage('Successfully joined channel: ' + channelName);
                    setupLocalVideo();
                    setIsJoined(true);
                },
                onUserJoined: (_connection: RtcConnection, uid: number) => {
                    setMessage('Remote user ' + uid + ' joined');
                    setRemoteUid(uid);
                },
                onUserOffline: (_connection: RtcConnection, uid: number) => {
                    setMessage('Remote user ' + uid + ' left the channel');
                    setRemoteUid(uid);
                },
            };
            agoraEngineRef.current?.registerEventHandler(eventHandler.current);
        };

        const setupVideoSDKEngine = async () => {
            try {
                if (Platform.OS === 'android') { await getPermission(); }
                agoraEngineRef.current = createAgoraRtcEngine();
                const agoraEngine = agoraEngineRef.current;
                await agoraEngine.initialize({ appId: appId });
            } catch (e) {
                console.error(e);
            }
        };

        const setupLocalVideo = () => {
            agoraEngineRef.current?.enableVideo();
            agoraEngineRef.current?.startPreview();
        };

        // Define the join method called after clicking the join channel button
        const join = async () => {
            if (isJoined) {
                return;
            }
            try {
                if (isHost) {
                    // Join the channel as a broadcaster
                    agoraEngineRef.current?.joinChannel(token, channelName, localUid, {
                        // Set the channel profile according to your use case 
                        channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
                        // Set user role to broadcaster
                        clientRoleType: ClientRoleType.ClientRoleBroadcaster,
                        // Publish audio collected by the microphone
                        publishMicrophoneTrack: true,
                        // Publish video collected by the camera
                        publishCameraTrack: true,
                        // Automatically subscribe to all audio streams
                        autoSubscribeAudio: true,
                        // Automatically subscribe to all video streams
                        autoSubscribeVideo: true,
                    });
                } else {
                    // Join the channel as an audience
                    agoraEngineRef.current?.joinChannel(token, channelName, localUid, {
                        // Set the channel profile according to your use case
                        channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
                        // Set user role to audience
                        clientRoleType: ClientRoleType.ClientRoleAudience,
                        // Only takes effect for the audience role
                        audienceLatencyLevel: AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
                        // Do not publish audio collected by the microphone
                        publishMicrophoneTrack: false,
                        // Do not publish video collected by the camera
                        publishCameraTrack: false,
                        // Automatically subscribe to all audio streams
                        autoSubscribeAudio: true,
                        // Automatically subscribe to all video streams
                        autoSubscribeVideo: true,
                    });
                }
            } catch (e) {
                console.log(e);
            }
        };

        // Define the leave method called after clicking the leave channel button
        const leave = () => {
            try {
                // Call leaveChannel method to leave the channel
                agoraEngineRef.current?.leaveChannel();
                setRemoteUid(0);
                setIsJoined(false);
                showMessage('Left the channel');
            } catch (e) {
                console.log(e);
            }
        };

        const cleanupAgoraEngine = () => {
            return () => {
                agoraEngineRef.current?.unregisterEventHandler(eventHandler.current!);
                agoraEngineRef.current?.release();
            };
        };

        // Render user interface
        return (
            <SafeAreaView style={styles.main}>
                <Text style={styles.head}>Agora RTC SDK quickstart</Text>
                <View style={styles.btnContainer}>
                    <Text onPress={join} style={styles.button}>
                        Join Channel
                    </Text>
                    <Text onPress={leave} style={styles.button}>
                        Leave Channel
                    </Text>
                </View>
                <View style={styles.btnContainer}>
                    <Text>Audience</Text>
                    <Switch
                        onValueChange={switchValue => {
                            setIsHost(switchValue);
                            if (isJoined) {
                                leave();
                            }
                        }}
                        value={isHost}
                    />
                    <Text>Host</Text>
                </View>
                <ScrollView
                    style={styles.scroll}
                    contentContainerStyle={styles.scrollContainer}>
                    {isJoined && isHost ? (
                        <React.Fragment key={localUid}>
                                <Text>Local user uid: {localUid}</Text>
                                <RtcSurfaceView canvas={{ uid: localUid, sourceType: VideoSourceType.VideoSourceCamera }} style={styles.videoView} />
                        </React.Fragment>
                    ) : (
                        <Text>Join a channel</Text>
                    )}
                    {isJoined && remoteUid !== 0 ? (
                        <React.Fragment key={remoteUid}>
                           <Text>Remote user uid: {remoteUid}</Text>
                           <RtcSurfaceView canvas={{ uid: remoteUid, sourceType: VideoSourceType.VideoSourceCamera }} style={styles.videoView} />
                        </React.Fragment>
                    ) : (
                        <Text>{isJoined && !isHost ? 'Waiting for remote user to join' : ''}</Text>
                    )}
                    <Text style={styles.info}>{message}</Text>
                </ScrollView>
            </SafeAreaView>
        );

        // Display information
        function showMessage(msg: string) {
            setMessage(msg);
        }
    };

    // Define user interface styles
    const styles = StyleSheet.create({
        button: {
            paddingHorizontal: 25,
            paddingVertical: 4,
            fontWeight: 'bold',
            color: '#ffffff',
            backgroundColor: '#0055cc',
            margin: 5,
        },
        main: { flex: 1, alignItems: 'center' },
        scroll: { flex: 1, backgroundColor: '#ddeeff', width: '100%' },
        scrollContainer: { alignItems: 'center' },
        videoView: { width: '90%', height: 200 },
        btnContainer: { flexDirection: 'row', justifyContent: 'center' },
        head: { fontSize: 20 },
        info: { backgroundColor: '#ffffe0', paddingHorizontal: 8, color: '#0000ff' },
    });

    const getPermission = async () => {
        if (Platform.OS === 'android') {
            await PermissionsAndroid.requestMultiple([
                PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
                PermissionsAndroid.PERMISSIONS.CAMERA,
            ]);
        }
    };

    export default App;
    ```
  </Accordion>
</Accordions>

### Create a user interface

Design a user interface for your project based on your application use-case. A basic interface consists of two view frames to display local and remote videos. Add **Join channel** and **Leave channel** buttons to enable the user to join and leave a channel. To create such an interface, refer to the following code:

### Sample code to create the user interface

```jsx
import React, { useState } from 'react';
// Import user interface elements
import {
    SafeAreaView,
    ScrollView,
    StyleSheet,
    Text,
    View,
    Switch,
} from 'react-native';

const localUid = 0; // Local user Uid, no need to modify

const App = () => {
    const [isJoined, setIsJoined] = useState(false); // Whether the local user has joined the channel
    const [isHost, setIsHost] = useState(true); // User role
    const [remoteUid, setRemoteUid] = useState(0); // Uid of the remote user
    const [message, setMessage] = useState(''); // User prompt message

    // Render user interface
    return (
        <SafeAreaView style={styles.main}>
            <Text style={styles.head}>Agora RTC SDK quickstart</Text>
            <View style={styles.btnContainer}>
                <Text onPress={join} style={styles.button}>
                    Join Channel
                </Text>
                <Text onPress={leave} style={styles.button}>
                    Leave Channel
                </Text>
            </View>
            <View style={styles.btnContainer}>
                <Text>Audience</Text>
                <Switch
                    onValueChange={switchValue => {
                        setIsHost(switchValue);
                        if (isJoined) {
                            leave();
                        }
                    }}
                    value={isHost}
                />
                <Text>Host</Text>
            </View>
            <ScrollView
                style={styles.scroll}
                contentContainerStyle={styles.scrollContainer}>
                {isJoined && isHost ? (
                    <React.Fragment key={localUid}>
                        <Text>Local user uid: {localUid}</Text>
                    </React.Fragment>
                ) : (
                    <Text>Join a channel</Text>
                )}
                {isJoined && remoteUid !== 0 ? (
                    <React.Fragment key={remoteUid}>
                       <Text>Remote user uid: {remoteUid}</Text>
                    </React.Fragment>
                ) : (
                    <Text>{isJoined && !isHost ? 'Waiting for remote user to join' : ''}</Text>
                )}
                <Text style={styles.info}>{message}</Text>
            </ScrollView>
        </SafeAreaView>
    );
};

// Define user interface styles
const styles = StyleSheet.create({
    button: {
        paddingHorizontal: 25,
        paddingVertical: 4,
        fontWeight: 'bold',
        color: '#ffffff',
        backgroundColor: '#0055cc',
        margin: 5,
    },
    main: { flex: 1, alignItems: 'center' },
    scroll: { flex: 1, backgroundColor: '#ddeeff', width: '100%' },
    scrollContainer: { alignItems: 'center' },
    videoView: { width: '90%', height: 200 },
    btnContainer: { flexDirection: 'row', justifyContent: 'center' },
    head: { fontSize: 20 },
    info: { backgroundColor: '#ffffe0', paddingHorizontal: 8, color: '#0000ff' },
});

export default App;
```

## Test the sample code

Take the following steps to test the sample code:

1. Update the values for `appId`, `token`, and `channelName` in your code.

2. Run the app.

   <CalloutContainer type="warning">
     <CalloutTitle>
       Caution
     </CalloutTitle>

     <CalloutDescription>
       Some simulators may not support all the functions of this project. Best practice is to run the project on a physical device.
     </CalloutDescription>
   </CalloutContainer>

   <Tabs defaultValue="android">
     <TabsList>
       <TabsTrigger value="android">
         Android
       </TabsTrigger>

       <TabsTrigger value="ios">
         iOS
       </TabsTrigger>
     </TabsList>

     <TabsContent value="android">
       To run the client on an Android device:

       1. Turn on the developer options of the Android device and connect the Android device to the computer through a USB cable.

       2. In the project root directory, execute `npx react-native run-android`.
     </TabsContent>

     <TabsContent value="ios">
       To run the client on an iOS device:

       1. Open the `ProjectName/ios/ProjectName.xcworkspace` folder using Xcode.

       2. Connect the iOS device to your computer through a USB cable.

       3. In Xcode, click the **Build and Run** button.
     </TabsContent>
   </Tabs>

   <CalloutContainer type="info">
     <CalloutTitle>
       Note
     </CalloutTitle>

     <CalloutDescription>
       For detailed steps on running the app on a real Android or iOS device, refer to [Running On Device](https://reactnative.dev/docs/running-on-device).
     </CalloutDescription>
   </CalloutContainer>

3. Launch the app and click the **Join channel** button to join a channel.

4. Invite a friend to install and run the client on a second device. Alternatively, use the [Web demo](https://webdemo.agora.io/basicVideoCall/index.html) to join the same channel. Once your friend joins the channel, you can see and hear each other.

## Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects of this product.

* If a firewall is deployed in your network environment, refer to [Connect with Cloud Proxy](/en/realtime-media/rtc/build/manage-connection-and-quality/cloud-proxy) to use Agora services normally.

### Next steps

After implementing the quickstart sample, read the following documents to learn more:

* To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see [Secure authentication with tokens](/en/realtime-media/rtc/build/authenticate-users/authentication-workflow).

### Sample project

Agora provides open source sample projects on [GitHub](https://github.com/AgoraIO-Extensions/react-native-agora/blob/main/example/src/examples) for your reference. Download or view the [JoinChannelVideo](https://github.com/AgoraIO-Extensions/react-native-agora/blob/main/example/src/examples/basic/JoinChannelVideo) project for a more detailed example.

### API reference

* [`createAgoraRtcEngine`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_createagorartcengine)

* [`initialize`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_initialize)

* [`registerEventHandler`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_addhandler)

* [`setChannelProfile`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_setchannelprofile)

* [`joinChannel`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_joinchannel2)

* [`leaveChannel`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_leavechannel2)

### Frequently asked questions

* [How can I fix black screen issues?](/en/api-reference/faq/quality/video_blank#black-screen-on-the-local-side)

* [Why can't I turn on the camera?](/en/api-reference/faq/quality/video_camera)

* [How can I listen for audience joining or leaving a channel?](/en/api-reference/faq/integration/audience_event)

* [How can I solve channel-related issues?](/en/api-reference/faq/integration/channel)

* [How can I set the log file?](/en/api-reference/faq/integration/set_log_file)

    
  
      
  
      
  
      
  
      
  
