Videoconferencing Quickstart

Build a video-conferencing app using NextJS and LiveKit's React Components Library.

This quickstart tutorial walks you through the steps to build a video-conferencing application using NextJS. It uses LiveKit's React Components Library to render the UI and communicate with LiveKit servers via WebRTC. By the end, you will have a basic video-conferencing application you can run with multiple participants.

Prerequisites

Steps

1. Set up your environment

Scaffold a NextJS project:

npx create-next-app@latest

Install LiveKit dependencies:

yarn add @livekit/components-react @livekit/components-styles livekit-client

Set the following environment variables in your .env.local file:

export LIVEKIT_URL=<your LiveKit server URL>
export LIVEKIT_API_KEY=<your API Key>
export LIVEKIT_API_SECRET=<your API Secret>

2. Create a token endpoint

Create a file src/app/api/token/route.ts and add the following code:

import { AccessToken } from 'livekit-server-sdk';
import type { AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
import { TokenResult } from '../../lib/types';
const apiKey = process.env.LIVEKIT_API_KEY;
const apiSecret = process.env.LIVEKIT_API_SECRET;
export default async function GET(req: Request) {
const { roomName, identity, name, metadata } = req.query;
const grant: VideoGrant = {
room: roomName,
roomJoin: true,
canPublish: true,
canPublishData: true,
canSubscribe: true,
};
const at = new AccessToken(apiKey, apiSecret, { identity });
at.ttl = '5m';
at.addGrant(grant);
const result: TokenResult = {
identity,
accessToken: at.toJwt(),
};
return Response.json(result, { status: 200 });
}

This sets up an API endpoint to generate LiveKit access tokens.

3. Use the VideoConference UI component

Create a file src/app/page.tsx and add the following code:

'use client';
import {
LiveKitRoom,
VideoConference,
formatChatMessageLinks,
useToken,
LocalUserChoices,
PreJoin,
} from '@livekit/components-react';
import {
Room,
RoomOptions,
VideoPresets,
setLogLevel,
} from 'livekit-client';
import Head from 'next/head';
import { useRouter } from 'next/router';
import * as React from 'react';
import { DebugMode } from '../../lib/Debug';
export default Page = () => {
const router = useRouter();
const { name: roomName } = router.query;
const [preJoinChoices, setPreJoinChoices] = React.useState<LocalUserChoices | undefined>(
undefined,
);
const preJoinDefaults = React.useMemo(() => {
return {
username: '',
videoEnabled: true,
audioEnabled: true,
};
}, []);
const handlePreJoinSubmit = React.useCallback((values: LocalUserChoices) => {
setPreJoinChoices(values);
}, []);
const onPreJoinError = React.useCallback((e: any) => {
console.error(e);
}, []);
const onLeave = React.useCallback(() => router.push('/'), []);
return (
<>
<Head>
<title>LiveKit Meet</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main data-lk-theme="default">
{roomName && !Array.isArray(roomName) && preJoinChoices ? (
<ActiveRoom
roomName={roomName}
userChoices={preJoinChoices}
onLeave={onLeave}
></ActiveRoom>
) : (
<div style={{ display: 'grid', placeItems: 'center', height: '100%' }}>
<PreJoin
onError={onPreJoinError}
defaults={preJoinDefaults}
onSubmit={handlePreJoinSubmit}
></PreJoin>
</div>
)}
</main>
</>
);
};
type ActiveRoomProps = {
userChoices: LocalUserChoices;
roomName: string;
region?: string;
onLeave?: () => void;
};
const ActiveRoom = ({ roomName, userChoices, onLeave }: ActiveRoomProps) => {
const tokenOptions = React.useMemo(() => {
return {
userInfo: {
identity: userChoices.username,
name: userChoices.username,
},
};
}, [userChoices.username]);
const token = useToken('/api/token', roomName, tokenOptions);
const router = useRouter();
const liveKitUrl = process.env.NEXT_PUBLIC_LIVEKIT_URL;
const roomOptions = React.useMemo((): RoomOptions => {
return {
videoCaptureDefaults: {
deviceId: userChoices.videoDeviceId ?? undefined,
resolution: VideoPresets.h1080,
},
publishDefaults: {
videoSimulcastLayers: [VideoPresets.h1080, VideoPresets.h540]
},
audioCaptureDefaults: {
deviceId: userChoices.audioDeviceId ?? undefined,
},
adaptiveStream: { pixelDensity: 'screen' },
};
}, [userChoices, hq]);
const room = React.useMemo(() => new Room(roomOptions), []);
return (
<>
{liveKitUrl && (
<LiveKitRoom
room={room}
token={token}
serverUrl={liveKitUrl}
connectOptions={{autoSubscribe: true}}
video={userChoices.videoEnabled}
audio={userChoices.audioEnabled}
onDisconnected={onLeave}
>
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
/>
<DebugMode />
</LiveKitRoom>
)}
</>
);
};

This sets up the main page component that renders the VideoConference UI.

4. Run the application

In your terminal, run:

npm run dev

This starts the app on localhost:3000.

Additional resources