Twilio Video Integration for Video Calls on Website
Twilio Video is a WebRTC platform with support for group rooms, recording, and phone integration. SDKs for JS, iOS, Android. Stands out for flexibility: you can build simple P2P calls or rooms up to 50 participants via Group Rooms.
Creating a Room and Access Token on Server
npm install twilio
import twilio from 'twilio';
const AccessToken = twilio.jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
const client = twilio(
process.env.TWILIO_ACCOUNT_SID!,
process.env.TWILIO_AUTH_TOKEN!
);
// Create room
async function createRoom(name: string) {
const room = await client.video.v1.rooms.create({
uniqueName: name,
type: 'group', // 'go' | 'peer-to-peer' | 'group' | 'group-small'
maxParticipants: 10,
recordParticipantsOnConnect: false,
statusCallback: `${process.env.APP_URL}/api/webhooks/twilio-video`,
statusCallbackMethod: 'POST',
});
return room.sid;
}
// Issue token to participant
function generateVideoToken(identity: string, roomName: string): string {
const token = new AccessToken(
process.env.TWILIO_ACCOUNT_SID!,
process.env.TWILIO_API_KEY!,
process.env.TWILIO_API_SECRET!,
{ identity, ttl: 3600 }
);
const grant = new VideoGrant({ room: roomName });
token.addGrant(grant);
return token.toJwt();
}
// API endpoint
app.post('/api/video/join', authenticate, async (req, res) => {
const { roomName } = req.body;
// Ensure room exists or create
try {
await client.video.v1.rooms(roomName).fetch();
} catch {
await createRoom(roomName);
}
const token = generateVideoToken(req.user.id, roomName);
res.json({ token, roomName });
});
React Component with Twilio Video JS SDK
npm install twilio-video
import { connect, Room, LocalVideoTrack } from 'twilio-video';
import { useEffect, useRef, useState } from 'react';
function TwilioVideoRoom({ token, roomName }: { token: string; roomName: string }) {
const [room, setRoom] = useState<Room | null>(null);
const [participants, setParticipants] = useState<string[]>([]);
const localVideoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
let connectedRoom: Room;
connect(token, {
name: roomName,
audio: true,
video: { width: 1280, height: 720 },
}).then((room) => {
connectedRoom = room;
setRoom(room);
// Display local video
const localTrack = [...room.localParticipant.videoTracks.values()][0]?.track;
if (localTrack && localVideoRef.current) {
localVideoRef.current.srcObject = new MediaStream([localTrack.mediaStreamTrack]);
}
// Handle participants
room.participants.forEach((p) => {
setParticipants(prev => [...prev, p.identity]);
});
room.on('participantConnected', (p) => {
setParticipants(prev => [...prev, p.identity]);
p.on('trackSubscribed', (track) => {
if (track.kind === 'video') {
const el = document.getElementById(`participant-${p.identity}`);
if (el) track.attach(el as HTMLVideoElement);
}
});
});
room.on('participantDisconnected', (p) => {
setParticipants(prev => prev.filter(id => id !== p.identity));
});
});
return () => {
connectedRoom?.disconnect();
};
}, [token, roomName]);
return (
<div className="grid grid-cols-2 gap-4">
<div className="relative">
<video ref={localVideoRef} autoPlay muted playsInline
className="w-full rounded-xl" />
<span className="absolute bottom-2 left-2 text-white text-sm bg-black/50 px-2 py-1 rounded">
You
</span>
</div>
{participants.map(identity => (
<div key={identity} className="relative">
<video id={`participant-${identity}`} autoPlay playsInline
className="w-full rounded-xl" />
<span className="absolute bottom-2 left-2 text-white text-sm bg-black/50 px-2 py-1 rounded">
{identity}
</span>
</div>
))}
</div>
);
}
Call Recording
// Enable recording for room
async function enableRoomRecording(roomSid: string) {
await client.video.v1.rooms(roomSid).recordings.create({
// Records all participants
});
}
// Get recording link after call
async function getRoomRecordings(roomSid: string) {
const recordings = await client.video.v1.rooms(roomSid).recordings.list();
return recordings.map(r => ({
sid: r.sid,
duration: r.duration,
url: `https://video.twilio.com/v1/Recordings/${r.sid}/Media`,
}));
}
Timeline
Basic Twilio Video integration + React component + Access Token—2–3 days. With participant management, recording, and webhooks—4–5 days.







