Next.js quickstart

Get started with LiveKit and Next.js

Note

This guide is compatible with Next.js 13 or later. On an older version? Check out the quickstart for Next.js 12.

Voice AI quickstart

To build your first voice AI app for Next.js, use the following quickstart and the starter app. Otherwise follow the getting started guide below.

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.

Create a Next.js app

If you're working with an existing app, skip to the next step.

npx create-next-app <your_app_name>

Change directory into your app directory:

cd <your_app_name>

Install LiveKit SDK

Install both frontend and backend LiveKit SDKs:

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 project URL.

In your project root create the file .env.local with the following contents. Do not commit this file because it contains your secrets!

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

Create token endpoint

Create a new file at /app/api/token/route.ts with the following content:

import { NextRequest, NextResponse } from 'next/server';
import { AccessToken } from 'livekit-server-sdk';
// Do not cache endpoint result
export const revalidate = 0;
export async function GET(req: NextRequest) {
const room = req.nextUrl.searchParams.get('room');
const username = req.nextUrl.searchParams.get('username');
if (!room) {
return NextResponse.json({ error: 'Missing "room" query parameter' }, { status: 400 });
} else if (!username) {
return NextResponse.json({ error: 'Missing "username" query parameter' }, { status: 400 });
}
const apiKey = process.env.LIVEKIT_API_KEY;
const apiSecret = process.env.LIVEKIT_API_SECRET;
const wsUrl = process.env.LIVEKIT_URL;
if (!apiKey || !apiSecret || !wsUrl) {
return NextResponse.json({ error: 'Server misconfigured' }, { status: 500 });
}
const at = new AccessToken(apiKey, apiSecret, { identity: username });
at.addGrant({ room, roomJoin: true, canPublish: true, canSubscribe: true });
return NextResponse.json(
{ token: await at.toJwt() },
{ headers: { "Cache-Control": "no-store" } },
);
}

Make a page in your web app

Make a new file at /app/room/page.tsx with the following content:

'use client';
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 function Page() {
// TODO: get user input for room and name
const room = 'quickstart-room';
const name = 'quickstart-user';
const [roomInstance] = useState(() => new Room({
// Optimize video quality for each participant's screen
adaptiveStream: true,
// Enable automatic audio/video quality optimization
dynacast: true,
}));
useEffect(() => {
let mounted = true;
(async () => {
try {
const resp = await fetch(`/api/token?room=${room}&username=${name}`);
const data = await resp.json();
if (!mounted) return;
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 used
as 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:3000/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.