---
sidebar_position: 7
sidebar_label: Live streams
---

# 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:

```ts
// 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 (`write` or `read`), whether they keep it, and what the host sends.
- **A visitor who is not signed in** comes with `user` null, and may only be a viewer.
- **The settings of the stream**, the third argument: `reactions`, `people` and `chat: { offline, links, fresh }` ([live streams in depth](../apps/streams.md#who-is-who)).

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:

```tsx
'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>
    );
}
```

- **`state`** is everything the screen of the stream draws: its phase, the audience, the chat, the pictures and their layout ([live streams in depth](../apps/streams.md#a-screen-of-your-own)).
- **`stream`** does what the page does: `chat.send( text )`, `react( 'heart' )`, `preview()`, `goLive()`, `end()`.
- **The picture** follows the stream as it changes. `video` stays `muted`, 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](../apps/streams.md).
