Live streams in Next.js
One person presents live to everyone watching: the picture, a chat beside it, reactions, and the count of who watches. The host goes live from the browser, a phone or OBS. Viewers may be signed in or not.
Who is who
Your server declares streams beside its rooms and calls, in lib/realtime.ts. The rule says who each person is in the stream:
// The show of the shop, show:mugs: Ann presents it, and everyone else watches and writes in its chat, signed in or not.
realtime.streams('show:{id}', (user, { id }) => {
if (id !== 'mugs') return false;
return user === ANN ? 'host' : 'viewer';
}, { reactions: true });
- The answer:
false,'host'or'viewer', or{ role, info, chat, mod, src, quality }for more: what others see of them, whether they write in the chat (writeorread), whether they keep it, and what the host sends. - A visitor who is not signed in comes with
usernull, and may only be a viewer. - The settings of the stream, the third argument:
reactions,peopleandchat: { offline, links, fresh }(live streams in depth).
The calls route (app/api/realtime/[action]/route.ts) answers streams too: nothing more to add.
The page
useStream( name ) joins the stream while the component is on the page, and gives its state as it changes:
'use client';
import { useCallback } from 'react';
import { useStream } from '@wordplus/realtime/react';
export function Show() {
const { stream, state, error } = useStream('show:mugs');
const picture = useCallback((video: HTMLVideoElement | null) => (video && stream ? stream.watch(video) : undefined), [stream]);
if (error) return <p>The show is not available here ({error}).</p>;
const live = state?.stream?.state === 'live';
return (
<section>
<video ref={picture} muted playsInline autoPlay />
{stream?.role === 'host' && !live && <button onClick={() => stream.goLive()}>Go live</button>}
{live && <p>{state.viewers} watching</p>}
<ul>{state?.chat.map((m) => <li key={m.id}><strong>{m.from.name}</strong> {m.text}</li>)}</ul>
</section>
);
}
stateis everything the screen of the stream draws: its phase, the audience, the chat, the pictures and their layout (live streams in depth).streamdoes what the page does:chat.send( text ),react( 'heart' ),preview(),goLive(),end().- The picture follows the stream as it changes.
videostaysmuted, and the sound plays from the page.
Show in app/(shop)/shop.tsx of the Example Shop is this, with End for the host and a field for the chat.
More
Reactions, keeping the chat, OBS, and the refusals: live streams in depth.