
Security News
Meet Socket at Black Hat Europe and BSides London 2025
Socket is heading to London! Stop by our booth or schedule a meeting to see what we've been working on.
@telnyx/video-react-native
Advanced tools
In this guide, you’ll learn how to get run and deploy Telnyx Meet
Just follow these steps:
Head to telnyx.com/sign-up to sign up for your free Telnyx account.
Once signed up you will have access to Telnyx Portal, where you can set up and manage your API KEY, and more.
Go to API Keys section and click on Create API Key button. It will generate a key for you. Copy and save this key in a safe place and don't share it with anyone it is a sensitive value.
You need this API Key to consume the API https://api.telnyx.com/v2/rooms to manage your room ids.

You should read this documentation video/Rooms to learn how to create a new video room id. When you get your roomId you can join in a video meet conference.
Install the package with:
npm install @telnyx/video-react-native --save
In android/settings.gradle file add following code
include ':react-native-webrtc'
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
In android/build.gradle file make minSdkVersion=24
In android/app/src/main/java/(package name)/MainApplication.java add following:
In import section add
import com.oney.WebRTCModule.WebRTCModulePackage;
In React package section add
packages.add(new WebRTCModulePackage());
In android/app/build.gradle file add
In dependency section add:
implementation project(':react-native-webrtc')
As long as you can import npm packages with a bundler like Webpack, you're ready to import Room and any other class required and begin.
You will also need some classes from react-native-webrtc such as RTCView which can be used to display a video stream:
import { Room, initialize, State } from '@telnyx/video-react-native';
import { RTCView, mediaDevices, MediaStream } from 'react-native-webrtc';
You will also need https://babeljs.io/docs/en/babel-plugin-proposal-async-generator-functions#installation to be able to use some of the async calls in the SDK.
In order to use native mobile features such as internet or record audio, you need to enable permissions for both platforms. You can follow these guides: Android iOS
General usage is the same as the vanilla web JS video implementation which is described here
And then use a permission library such as react-native-permissions to verify that the permissions are enabled at runtime.
The key difference between the Web and React Native SDK is how we attach streams to a view. With React Native we are no longer using DOM elements, but rather an RTCView:
const [localStream, setStream] = useState<string>('');
...
const onPressPublish = async () => {
try {
let callerStream = await mediaDevices.getUserMedia({
audio: true,
video: true,
});
console.log('got local stream using getUserMedia...');
// Get audio and video tracks from the MediaStream's
// since the sdk works with MediaStreamTrack
let callerAudioTrack = callerStream.getAudioTracks()[0];
let callerVideoTrack = callerStream.getVideoTracks()[0];
await room.addStream('self', {
audio: callerAudioTrack,
video: callerVideoTrack,
});
setStream(callerStream.toURL());
} catch (e) {
console.error("Unable to publish stream...");
}
};
....
<SafeAreaView style={styles.localStreamBox}>
{
localStream ?
<RTCView
mirror={true}
objectFit={'contain'}
streamURL={localStream!}
style={{ width: '100%', height: '100%' }}
zOrder={1}
/> : null
}
</SafeAreaView>
Below is an adapted version of the following web example adapted for React Native with this library
import * as React from 'react';
import { StyleSheet, View, Alert, Platform, StatusBar, SafeAreaView, Button } from 'react-native';
import { RTCView, mediaDevices, MediaStream } from 'react-native-webrtc';
import { useEffect, useState } from 'react';
import { Room, initialize, State } from 'telnyx-video-react-native';
import { FlatGrid } from 'react-native-super-grid';
import { requestMultiple, PERMISSIONS } from 'react-native-permissions';
import Config from "react-native-config";
//create .env file in root of example and store Room ID and API Key
let ROOM_ID = Config.ROOM_ID
let API_KEY = Config.API_KEY
export default function App() {
const [localStream, setStream] = useState<string>('');
const [room, setRoom] = useState<typeof Room>();
const [participantStreamMaps, setParticipantStreamMaps] = useState<Map<String, MediaStream>>(new Map<string, MediaStream>())
const updateStreamMap = (k: string, v: MediaStream) => {
setParticipantStreamMaps(new Map(participantStreamMaps.set(k, v)));
}
const deleteFromStreamMap = (v: string) => {
participantStreamMaps.delete(v)
let map = new Map(participantStreamMaps)
map.delete(v)
setParticipantStreamMaps(new Map(map));
}
const [data, setData] = useState();
const [isLoading, setLoading] = useState(true);
useEffect(() => {
if (API_KEY == undefined || ROOM_ID == undefined) {
throw Error('Please create a .env file and add your ROOM_ID and API_KEY');
}
verifyPermissions();
getToken();
}, []);
const getToken = async () => {
const requestTokenUrl = `https://api.telnyx.com/v2/rooms/${ROOM_ID}/actions/generate_join_client_token`;
var tokenReceived;
try {
const response = await fetch(requestTokenUrl, {
body: '{"refresh_token_ttl_secs":3600,"token_ttl_secs":600}',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${API_KEY}`,
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
},
method: 'POST',
});
const json = await response.json();
setData(json);
tokenReceived = json;
} catch (error) {
console.error(error);
} finally {
setLoading(false);
makeCall(tokenReceived.data.token);
}
};
async function makeCall(token: string) {
let room;
console.log(`Token being provided: ${token}`);
// Replace this with the client token you generated previously.
const context = '{"id":99999,"username":"React Native User"}';
room = await initialize({
roomId: ROOM_ID,
clientToken: token,
context: context,
logLevel: 'DEBUG',
});
setRoom(room);
// you are connected to the room
room.on("connected", (state) => {
let receivedState = state as State
console.log(`connected to the room... ` + JSON.stringify(receivedState));
// Once we are connected we can access the list of participants that are already connected to the room.
const remoteParticipants = state.participants;
remoteParticipants.forEach((item) => {
const remoteParticipant = state.participants.get(item.participantId);
console.log(`participant connected ` + item);
});
// We can also access the list of streams available and subscribe to them if need it.
state.streams.forEach((stream) => {
if (stream.participantId === room.getLocalParticipant().id) {
return;
}
room.addSubscription(stream.participantId, stream.key, {
audio: true,
video: true,
});
});
});
// a remote participant has joined the room
room.on("participant_joined", (participantId, state) => {
const remoteParticipant = state.participants.get(participantId);
console.log(`participant ${remoteParticipant.context} joined...`);
});
// a stream has been published to the room
room.on("stream_published", async (participantId, streamKey, state) => {
// ignore streams that are published by the local participant
// we only care about remote stream from other remote participant
let participant = state.participants.get(participantId);
if (participant.origin === "local") {
return;
}
// the remote stream is identified using the participantId and streamKey
// you need to subscribe to a remote stream in order to access it's `MediaStreamTrack`s
await room.addSubscription(participantId, streamKey, {
audio: true,
video: true
});
console.log(
`subscription to the: ${participantId} ${streamKey} has been added...`
);
});
// a subscription to a remote stream has started
room.on("subscription_started", (participantId, streamKey, state) => {
console.log(
`subscription to the: ${participantId} ${streamKey} stream started...`
);
let receivedState = state as State
console.log(`Subscription started :: State .. ` + JSON.stringify(receivedState));
// use a helper method to easily access a remote participants' stream
let remoteStream = room.getParticipantStream(participantId, streamKey);
let remoteMediaStream = new MediaStream([
remoteStream.audioTrack,
remoteStream.videoTrack
]);
// Add participant stream from participant stream array
updateStreamMap(participantId, remoteMediaStream)
});
// a subscription to a remote stream has ended
room.on("subscription_ended", (participantId, streamKey, state) => {
console.log(
`subscription to the: ${participantId} ${streamKey} stream ended...`
);
deleteFromStreamMap(participantId)
});
await room.connect();
}
const verifyPermissions = async () => {
if (Platform.OS == 'android') {
let perm = [
PERMISSIONS.ANDROID.CAMERA,
PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
PERMISSIONS.ANDROID.RECORD_AUDIO,
PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
];
let permissionStatuses = await requestMultiple(perm);
console.log('obj', permissionStatuses);
const result = permissionStatuses[perm[0]];
if (result !== 'granted') {
Alert.alert(
'Insufficient permissions!',
'You need to grant camera and library access permissions to use this app.',
[{ text: 'Okay' }],
);
return false;
}
return true;
} else {
let perm = [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE];
let permissionStatuses = await requestMultiple(perm);
console.log('obj', permissionStatuses);
const result = permissionStatuses[perm[0]];
if (result !== 'granted') {
Alert.alert(
'Insufficient permissions!',
'You need to grant camera and library access permissions to use this app.',
[{ text: 'Okay' }],
);
return false;
}
return true;
}
};
const onPressPublish = async () => {
try {
let callerStream = await mediaDevices.getUserMedia({
audio: true,
video: true,
});
console.log('got local stream using getUserMedia...');
// Get audio and video tracks from the MediaStream's
// since the sdk works with MediaStreamTrack
let callerAudioTrack = callerStream.getAudioTracks()[0];
let callerVideoTrack = callerStream.getVideoTracks()[0];
await room.addStream('self', {
audio: callerAudioTrack,
video: callerVideoTrack,
});
setStream(callerStream.toURL());
} catch (e) {
console.error("Unable to publish stream...");
}
};
return (
<>
<StatusBar barStyle="dark-content" />
<View style={styles.box}>
<Button
title="Start"
onPress={onPressPublish} />
</View>
{
<FlatGrid
data={Array.from(participantStreamMaps.values())}
style={styles.gridView}
itemDimension={130}
spacing={10}
renderItem={({ item }) =>
<SafeAreaView style={styles.remoteStreamBox}>
<RTCView
mirror={false}
objectFit={'contain'}
streamURL={item.toURL()}
style={{ width: '100%', height: '100%' }}
zOrder={1}
/>
</SafeAreaView>}
keyExtractor={item => item.toURL()}
/>
}
<SafeAreaView style={styles.localStreamBox}>
{
localStream ?
<RTCView
mirror={true}
objectFit={'contain'}
streamURL={localStream!}
style={{ width: '100%', height: '100%' }}
zOrder={1}
/> : null
}
</SafeAreaView>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
gridView: {
marginTop: 10,
flex: 1,
},
box: {
width: 60,
height: 60,
marginVertical: 20,
},
localStreamBox: {
width: 400,
height: 300,
marginVertical: 20,
},
remoteStreamBox: {
width: 200,
height: 125,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
FAQs
Telnyx Video React Native SDK
The npm package @telnyx/video-react-native receives a total of 4 weekly downloads. As such, @telnyx/video-react-native popularity was classified as not popular.
We found that @telnyx/video-react-native demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 10 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket is heading to London! Stop by our booth or schedule a meeting to see what we've been working on.

Security News
OWASP’s 2025 Top 10 introduces Software Supply Chain Failures as a new category, reflecting rising concern over dependency and build system risks.

Research
/Security News
Socket researchers discovered nine malicious NuGet packages that use time-delayed payloads to crash applications and corrupt industrial control systems.