Skip to main content

Next.js SDK — Getting Started

Use @video/video-client-react in a Next.js app — App Router or Pages Router. The React hooks and components are the same as the React guide; the one Next.js-specific step is keeping the SDK out of server rendering. This guide gets you to a working broadcaster in under 10 minutes.

Prerequisites

Grab these from your account before you start:

  • An authentication token (JWT) — a broadcaster token to stream, a viewer token to watch.
  • A stream key for your stream.
  • Your backend endpoint, e.g. https://your-subdomain.example.com.

Next.js 13+ (App Router) or 12+ (Pages Router), on React 16.8+.

1. Configure the registry

Add the private registry to your project's .npmrc:

@video:registry=https://npm-packages.example.com/

2. Install

npm install @video/video-client-react
The SDK is browser-only

The SDK uses WebRTC and browser media APIs (navigator.mediaDevices, RTCPeerConnection), which don't exist on the server. Rendering it during SSR throws window is not defined or navigator is not defined. The fix is the same in both routers: render the video component on the client only, via next/dynamic with ssr: false.

3. Create the video component

Put the broadcaster in its own component. The 'use client' directive is required by the App Router and is harmless under the Pages Router, so a single file works for both.

components/Broadcaster.tsx
'use client';

import React, { useMemo } from 'react';
import {
usePreviewPlayer,
useAuthClient,
useCreateCall,
useBroadcast
} from '@video/video-client-react/hooks';
import {
MediaStreamControllerAPIProvider,
PlayerAPIProvider
} from '@video/video-client-react/context';
import { Video } from '@video/video-client-react/components';

export default function Broadcaster() {
const { mediaStreamController, previewPlayer } = usePreviewPlayer();

const authClient = useAuthClient('your-broadcaster-token');

// useCreateCall and useBroadcast pin their inputs at first render, so memoize
// these — a fresh object literal every render means later changes are
// silently ignored instead of recreating the call.
const callOptions = useMemo(() => ({
streamKey: 'your-stream-key',
backendEndpoints: ['https://your-subdomain.example.com'],
auth: authClient,
user: { userId: 'user-1', displayName: 'Broadcaster' }
}), [authClient]);

const broadcastOptions = useMemo(() => ({ streamName: 'default' }), []);

const { call, status, error } = useCreateCall(authClient == null ? null : callOptions);
useBroadcast(call, mediaStreamController, broadcastOptions);

if (status === 'error') {
return <div>Could not start the call: {String(error)}</div>;
}

return (
<MediaStreamControllerAPIProvider mediaStreamController={mediaStreamController}>
<PlayerAPIProvider player={previewPlayer}>
<div>
<h1>My First Livestream</h1>
<Video /> {/* Camera preview */}
{call != null && <p>Call ID: <code>{call.id}</code></p>}
</div>
</PlayerAPIProvider>
</MediaStreamControllerAPIProvider>
);
}
What happened to useCallControls?

Earlier versions of this guide imported a useCallControls hook from the SDK. The SDK does not export one — it is a helper defined inside the demo app, so that import could never resolve. Start and stop are driven by useCreateCall and useBroadcast instead, as above. There are no ready-made buttons: build your own from the lifecycle functions those two hooks return — start and dispose from useCreateCall, start and stop from useBroadcast.

4. Load it client-side only

Import the component with next/dynamic and ssr: false so Next.js never tries to render it on the server.

app/broadcast/page.tsx
import dynamic from 'next/dynamic';

// ssr: false keeps the WebRTC/browser-only SDK off the server.
const Broadcaster = dynamic(() => import('../../components/Broadcaster'), {
ssr: false,
loading: () => <p>Loading…</p>
});

export default function BroadcastPage() {
return <Broadcaster />;
}

Run npm run dev and open /broadcast — you'll see your camera preview and, once the call connects, the call ID to hand to your viewer.

5. Add a viewer

A viewer follows the exact same pattern: build the component with the React viewer code, then load it through dynamic(..., { ssr: false }). Reuse the Viewer component from the React guide and wire it into a page the same way as above.

Next steps