Using a newer version? See the updated quickstart for Next.js 13+.
Getting started guide
This guide walks you through the steps to build a video-conferencing application using NextJS. It uses the LiveKit 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.
Install LiveKit SDK
Install the necessary LiveKit SDKs for the web frontend and for the server APIs:
yarn add livekit-server-sdk @livekit/components-react @livekit/components-styles
Keys and Configuration
To start, your app needs an LiveKit API key and secret, as well as your LiveKit server URL.
Create a new file at .env.development.local
with the following content. Do not commit this file!
LIVEKIT_API_KEY=<your API Key>LIVEKIT_API_SECRET=<your API Secret>NEXT_PUBLIC_LIVEKIT_URL=<your LiveKit server URL>
Create token endpoint
Create a new file at /api/get_lk_token.ts
with the following content:
import { NextApiRequest, NextApiResponse } from 'next';import { AccessToken } from 'livekit-server-sdk';// Do not cache endpoint result// See https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidateexport const revalidate = 0;export default async function handler(req: NextApiRequest, res: NextApiResponse) {const room = req.query.room as string;const username = req.query.username as string;if (req.method !== 'GET') {return res.status(400).json({ error: 'Invalid method' });} else if (!room) {return res.status(400).json({ error: 'Missing "room" query parameter' });} else if (!username) {return res.status(400).json({ error: 'Missing "username" query parameter' });}const apiKey = process.env.LIVEKIT_API_KEY;const apiSecret = process.env.LIVEKIT_API_SECRET;const wsUrl = process.env.NEXT_PUBLIC_LIVEKIT_URL;if (!apiKey || !apiSecret || !wsUrl) {return res.status(500).json({ error: 'Server misconfigured' });}const at = new AccessToken(apiKey, apiSecret, { identity: username });at.addGrant({ room, roomJoin: true, canPublish: true, canSubscribe: true });res.setHeader({ 'Cache-Control': 'no-store' });res.status(200).json({ token: await at.toJwt() });}
Make a page in your web app
Make a new file at /pages/room.tsx
with the following content:
import {ControlBar,GridLayout,ParticipantTile,RoomAudioRenderer,useTracks,RoomContext,} from '@livekit/components-react';import { Room, Track } from 'livekit-client';import '@livekit/components-styles';import { useEffect, useState } from 'react';export default () => {// TODO: get user input for room and nameconst room = 'quickstart-room';const name = 'quickstart-user';const [token, setToken] = useState('');const [roomInstance] = useState(() => new Room({// Optimize video quality for each participant's screenadaptiveStream: true,// Enable automatic audio/video quality optimizationdynacast: true,}));useEffect(() => {let mounted = true;(async () => {try {const resp = await fetch(`/api/get_lk_token?room=${room}&username=${name}`);const data = await resp.json();if (!mounted) return;setToken(data.token);if (data.token) {await roomInstance.connect(process.env.NEXT_PUBLIC_LIVEKIT_URL, data.token);}} catch (e) {console.error(e);}})();return () => {mounted = false;roomInstance.disconnect();};}, [roomInstance]);if (token === '') {return <div>Getting token...</div>;}return (<RoomContext.Provider value={roomInstance}><div data-lk-theme="default" style={{ height: '100dvh' }}>{/* Your custom component with basic video conferencing functionality. */}<MyVideoConference />{/* The RoomAudioRenderer takes care of room-wide audio for you. */}<RoomAudioRenderer />{/* Controls for the user to start/stop audio, video, and screen share tracks */}<ControlBar /></div></RoomContext.Provider>);};function MyVideoConference() {// `useTracks` returns all camera and screen share tracks. If a user// joins without a published camera track, a placeholder track is returned.const tracks = useTracks([{ source: Track.Source.Camera, withPlaceholder: true },{ source: Track.Source.ScreenShare, withPlaceholder: false },],{ onlySubscribed: false },);return (<GridLayout tracks={tracks} style={{ height: 'calc(100vh - var(--lk-control-bar-height))' }}>{/* The GridLayout accepts zero or one child. The child is usedas a template to render all passed in tracks. */}<ParticipantTile /></GridLayout>);}
Load the page and connect
Start your server with:
yarn dev
And then open localhost:8000/room
in your browser.
Next steps
The following resources are useful for getting started with LiveKit on Next.js.
Generating tokens
Guide to generating authentication tokens for your users.
Realtime media
Complete documentation for live video and audio tracks.
Realtime data
Send and receive realtime data between clients.
JavaScript SDK
LiveKit JavaScript SDK on GitHub.
React components
LiveKit React components on GitHub.
JavaScript SDK reference
LiveKit JavaScript SDK reference docs.
React components reference
LiveKit React components reference docs.