Headless / Realtime
Video meeting
Build video calls & devices with the room's shared state and capability-aware controls.
48 public parts · 1examples
@slices.sh/realtime/headlessView as Markdown ↗Your design. Our behavior.
Explore the tree to see what each part controls. Gates reveal the relevant state; roots outline their scope.
Room.Root · Scope
Installation
Choose what your component needs. Run these commands in an initialized Next.js app.
Before you start
The steps below run in a Next.js app with shadcn set up (it has a components.json). In an existing Next.js app, such as one from create-next-app, set shadcn up first:
npx shadcn@latest init -t next -b base -p novaOr start fresh: the slices CLI creates the app and sets it up.
npx slices.sh installInstall the service registry
Adds services/index.ts, which import "@/services" loads. A provider setup writes the same file, so skip this if your app has one.
npx add servicesInstall the headless library
npmAdd the React parts to your app, then compose them with your own elements.
npm install @slices.sh/realtime@^0.1.6Import from @slices.sh/realtime/headless.
Install the service
Connect the parts to a provider. Skip this step if this service is already configured in your app.
npx add realtime.p2p-meshAdds service configuration and API routes to your app, with the provider dependencies. For a headless composition, add import "@/services" after "use client".
The slices CLI installs the service and provisions its required resources in one command.
Provider setup notes
Start small
Parts bring the behavior. Use their default elements, or supply your own with render.
"use client"import "@/services"import { Room } from "@slices.sh/realtime/headless"export function Example() { return ( <Room.Root room="studio"> <Room.Meeting> <Room.Stage> <Room.Tiles> <Room.Tile> <Room.TileVideo /> <Room.TileAudio /> <Room.PeerName /> </Room.Tile> </Room.Tiles> </Room.Stage> <Room.MediaToggle kind="audio">Microphone</Room.MediaToggle> <Room.MediaToggle kind="video">Camera</Room.MediaToggle> <Room.MediaDenied> Allow access to your camera and microphone. </Room.MediaDenied> </Room.Meeting> </Room.Root> )}Configure realtime. Media, captions and moderation controls hide when the provider does not grant their capability.
API reference
Every public part used by this experience is listed below. Gates render children without adding an element. Element parts accept native element props and render; function children receive the state declared in their props. Scopes provide context rather than a visual element.
Room.Meetingscope
A call's way of looking at the room: the layout this person chose, the tiles they pinned, and who has the floor. Local to this browser — nobody's layout moves anybody else's. Around the stage and the controls both, so a layout menu in a top bar and the stage it changes read the one set of choices.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
pinLimitHow many tiles may be pinned at once; the oldest pin gives way past it. | number | 3 |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Also inherits Partial<Pick<LayoutChoices, "mode" | "maxTiles" | "selfView" | "hideNoVideo">>. See TypeScript for the complete declaration.
Source: @slices.sh/realtime/src/headless/meeting.tsx
Room.CallFramecontent · <div>
Meeting is this browser's view of a call: its layout, its pins, who has the floor. CallFrame is what full screen fills and where Meet's shortcuts listen. Stage measures the box, Tiles repeats tiles and Tile is one, with TrackIs kind="camera" | "screen" | "board". Pin and Pinned pin a tile. Overflow is the +N tile. LayoutMode, CurrentLayout, TileLimit, HideNoVideo and SelfView set the layout. FullScreen and PictureInPicture take the call elsewhere; ShortcutsSheet and ShowShortcuts list the keys.
Required scope
Room.CallFrame must be inside <Room.Root>.
Props
3 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
spaceToTalkHold Space to talk, anywhere in the call but a field or a button — a voice room's key. | boolean | false |
childrenContent rendered inside this part. | ReactNode | ((state: { fullScreen: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/call-frame.tsx
Room.SidePanelsscope
Panels beside a call — one open at a time, chosen by each person for themselves. Inside a `Room.CallFrame`, ⌃⌘C opens the chat, ⌃⌘P the people, ⌃⌘B the whiteboard and ⌃⌘T the transcript, where the call has them and draws their `Room.SidePanel`.
Props
3 documented props
| Prop | Type | Default |
|---|---|---|
panelsRequiredThe panels this call has: what its toggles and shortcuts may open. | readonly string[] | — |
initial | string | null | null |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/side-panels.tsx
Room.PanelAreacontent · <div>
What holds the call and its panel: `data-state` names the open panel, for the layout to follow.
Required scope
Room.PanelArea must be inside <Room.SidePanels>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { open: string | null }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/panel-area.tsx
Room.Stagecontent · <div>
The call's stage: one measured box every tile is laid out in. Tiles are one flat list of tracks positioned inside it, with rects from a pure layout function — so a camera moving from the grid to the strip, or a screen taking the stage, is the same element gliding, and a playing `<video>` is never remounted. `data-shape` says what the layout came out as (`solo`, `one-on-one`, `grid`, `focus`, `spotlight`). Whoever the room's follow is on takes the stage like a pinned tile, ahead of the pins. → hooks/realtime/track-layout.ts
Required scope
Room.Stage must be inside <Room.Meeting>.
Room.Stage must be inside <Room.Root>.
Props
4 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
insetRoom kept free inside the edges, in pixels, so a tile's ring is never clipped. | number | 0 |
insetBottomRoom kept free at the bottom, for captions or a control bar laid over the stage. | number | 0 |
childrenContent rendered inside this part. | ReactNode | ((state: { shape: string }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/stage.tsx
Room.Tilesscope
Everything on the stage, one scope per track: a camera per person, and a screen for each person sharing one. Keyed by track and never regrouped, so no tile changes parent when the layout does.
Required scope
Room.Tiles must be inside <Room.Stage>.
Room.Tiles must be inside <Room.Root>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
groupWhat the tiles are grouped in: a Motion presence group lets somebody leave, not vanish. | ComponentType<{ children?: ReactNode }> | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tiles.tsx
Room.Tilecontent · <div>
One track on the stage — a face, a shared screen — at the rect the layout gave it. Absolutely placed inside `Room.Stage` and moved on the shared layout spring whenever the layout changes, so a tile glides from the grid to the strip and its `<video>` keeps playing: a tile never changes parent. `data-kind` is what it shows, `data-slot` where it sits (`grid`, `stage`, `strip`, `float`, `hidden`), `data-pinned` whether you pinned it and `data-followed` whether its person is the one you follow, for a skin to outline. A hidden tile is inert and out of the accessibility tree; it still exists, so a camera coming back into view does not have to start again. `data-active` says the person is reachable — a connection is open — rather than only listed. A camera tile carries its person's voice as `--level`, for a ring drawn in CSS. Enter or a double click pins it. The floating self-view can be dragged to any corner.
Required scope
Room.Tile must be inside <Room.Tiles>.
Room.Tile must be inside <Room.Stage>.
Room.Tile must be inside <Room.Meeting>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: TileState) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tile.tsx
Room.TrackIsgate
The tile in scope shows this kind of track: a camera, a screen, the board.
Required scope
Room.TrackIs must be inside <Room.Tiles>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
kindRequired | "camera" | "screen" | "board" | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/track-is.tsx
Room.TileScreencontent · <video>
What one peer is sharing. Its own part rather than a second use of `Room.TileVideo`, because a camera and a screen are two pictures and an element plays the first video track it is handed: one element for both is one of them showing and the other silently lost. Rendered only while that peer says it is sharing. → docs/rules/capability-honesty.md Silent, like every tile: a shared screen's sound, where a browser gives any, is that peer's audio and belongs to `Room.TileAudio`.
Required scope
Room.TileScreen must be inside <Room.Peers>.
Props
2 documented props · Native <video> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tile-screen.tsx
Room.TileVideocontent · <video>
One track's picture: a peer's camera on a camera tile, the screen they share on a screen tile. Outside a stage, where a tile is a person, it is their camera. Rendered only while that peer says the track is on, because a `<video>` with no track is a black rectangle and a grid of them looks like an outage. What the peer *says* decides, not whether a track happens to exist. → docs/rules/capability-honesty.md Silent, always: the voice is `Room.TileAudio`'s job, so an unmuted tile would play it twice, and your own unmuted tile is your microphone in your own speakers. `data-state="mine"` on your own camera, which a skin flips: a self-view is a mirror. A screen is never flipped, yours included — a mirrored slide is a slide nobody can read.
Required scope
Room.TileVideo must be inside <Room.Peers> or <Room.Tiles>.
Props
2 documented props · Native <video> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tile-video.tsx
Room.TileAudiocontent · <audio>
One peer's sound. Never your own: playing your own microphone back is a howl, and it is the first thing anybody building a room gets wrong.
Required scope
Room.TileAudio must be inside <Room.Peers>.
Props
2 documented props · Native <audio> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tile-audio.tsx
Room.CameraOffgate
This peer has no picture on screen: where the initials go instead of one. A camera somebody said was off and a camera nobody has heard about are the same thing to look at — there is no picture either way — so `null` belongs here with `false`. It is the badges that must not guess. → docs/rules/capability-honesty.md
Required scope
Room.CameraOff must be inside <Room.Peers>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/camera-off.tsx
Room.Mutedgate
This peer says its microphone is off. What it says, not what its audio sounds like: silence is not mute, and a tile that guessed would label every pause. → docs/rules/capability-honesty.md And said, rather than merely not said: `micOn` is `null` for the second before a peer's first control message and for as long as nobody can reach them, and a badge there was a claim nobody had made — every new face arrived wearing a crossed-out microphone.
Required scope
Room.Muted must be inside <Room.Peers>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/muted.tsx
Room.Speakinggate
This peer is making a noise a person would call talking. Measured, not announced: a peer cannot tell you it is speaking any faster than it can send you the sound. From the level the page already reads for rings, on one shared analyser, or from the transport where it knows. → ./use-talking.ts
Required scope
Room.Speaking must be inside <Room.Peers>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/speaking.tsx
Room.LinkQualitycontent · <span>
A badge for somebody whose connection is struggling: the answer to "did they freeze, or did I?". Nothing while their link is fine, and nothing before it has been measured or where it cannot be — a badge that always shows teaches everybody to stop reading it. `data-state` is `fair` or `poor`; its children get the same, for words that say which.
Required scope
Room.LinkQuality must be inside <Room.Peers>.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { quality: Quality }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/link-quality.tsx
Room.Pinaction · <button>
Pin this tile for yourself, or unpin it. Pinned tiles take the stage, oldest first, and nobody else's view moves. `aria-pressed` and `data-active` while pinned.
Required scope
Room.Pin must be inside <Room.Tiles>.
Room.Pin must be inside <Room.Meeting>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { pinned: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/pin.tsx
Room.Pinnedgate
You pinned the tile in scope.
Required scope
Room.Pinned must be inside <Room.Tiles>.
Room.Pinned must be inside <Room.Meeting>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/pinned.tsx
Room.HandRaisedgate
This peer has a hand up, and where they are in the queue of raised hands, from 1.
Required scope
Room.HandRaised must be inside <Room.Peers> or <Room.Tiles>.
Room.HandRaised must be inside <Room.Root>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
group | GateGroupComponent | — |
childrenContent rendered inside this part. | ReactNode | ((state: { place: number }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/hand-raised.tsx
Room.TileReactiongate
The emoji this peer sent last, while it is still in the air: a badge on their tile. Each emoji is an arrival of its own, so a second one replaces the first rather than relabelling it.
Required scope
Room.TileReaction must be inside <Room.Tiles>.
Room.TileReaction must be inside <Room.Root>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
group | GateGroupComponent | — |
childrenContent rendered inside this part. | ReactNode | ((state: { emoji: string; id: string }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tile-reaction.tsx
Room.Overflowcontent · <div>
The last cell of a full stage: how many are not shown, and who. Nothing while everybody fits. Placed and moved like a tile, on the same spring. Drawn even for a mesh, which stops long before it needs one, so a transport that carries more people later needs no new screen.
Required scope
Room.Overflow must be inside <Room.Stage>.
Room.Overflow must be inside <Room.Root>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: OverflowState) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/overflow.tsx
Room.SidePanelcontent · <aside>
One panel beside the call, drawn only while it is the open one. Escape closes it, from anywhere inside it — the way out of a panel a person opened by accident. While mounted it is what makes its panel reachable: inside a gate that hides it, its toggle and shortcut do nothing.
Required scope
Room.SidePanel must be inside <Room.SidePanels>.
Props
3 documented props · Native <aside> attributes supported
| Prop | Type | Default |
|---|---|---|
panelRequired | string | — |
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/side-panel.tsx
Room.PanelToggleaction · <button>
Opens one panel beside the call, or closes it. `aria-expanded` and `data-active` while open.
Required scope
Room.PanelToggle must be inside <Room.SidePanels>.
Props
3 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
panelRequired | string | — |
childrenContent rendered inside this part. | ReactNode | ((state: { open: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/panel-toggle.tsx
Room.LocalVideocontent · <video>
Your own picture. Muted, always: an unmuted local tile is your microphone coming out of your own speakers.
Required scope
Room.LocalVideo must be inside <Room.Root>.
Props
2 documented props · Native <video> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/local-video.tsx
Room.LayoutModeaction · <button>
Choose how the stage is laid out. `aria-pressed` and `data-active` on the current one.
Required scope
Room.LayoutMode must be inside <Room.Meeting>.
Props
3 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
modeRequired | "auto" | "tiled" | "spotlight" | "sidebar" | — |
childrenContent rendered inside this part. | ReactNode | ((state: { active: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/layout-mode.tsx
Room.CurrentLayoutcontent · <span>
The layout in use, for a menu's trigger to name. `data-state` is the mode.
Required scope
Room.CurrentLayout must be inside <Room.Meeting>.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { mode: LayoutMode }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/current-layout.tsx
Room.TileLimitinput · <input>
How many tiles the stage shows before the rest are counted on a "+N" tile.
Required scope
Room.TileLimit must be inside <Room.Meeting>.
Props
2 documented props · Native <input> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { value: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/tile-limit.tsx
Room.HideNoVideoaction · <button>
Leave out the tiles of people whose camera is off, unless they are pinned. A switch.
Required scope
Room.HideNoVideo must be inside <Room.Meeting>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { on: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/hide-no-video.tsx
Room.SelfViewaction · <button>
Where your own picture goes: chosen for you, in the grid, floating, or nowhere.
Required scope
Room.SelfView must be inside <Room.Meeting>.
Props
3 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
viewRequired | "auto" | "tile" | "float" | "hidden" | — |
childrenContent rendered inside this part. | ReactNode | ((state: { active: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/self-view.tsx
Room.RaiseHandaction · <button>
Put your hand up, or down. Everybody sees it, in the order hands went up. `aria-pressed` and `data-active` while it is up.
Required scope
Room.RaiseHand must be inside <Room.Root>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { raised: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/raise-hand.tsx
Room.Handsscope
The queue of raised hands, first raised first, each in its peer's scope.
Required scope
Room.Hands must be inside <Room.Root>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/hands.tsx
Room.Reactaction · <button>
Send an emoji to the room: it rises from the edge for everybody, with your name.
Required scope
Room.React must be inside <Room.Root>.
Props
3 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
emojiRequired | string | — |
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/react.tsx
Room.Flightsscope
Emoji in the air, each in its own scope with its lane and its sender. A sender keeps one lane while their emoji are flying, so two people's names never land on each other, and only the first emoji of a burst carries the name. → hooks/realtime/use-reactions.ts
Required scope
Room.Flights must be inside <Room.Root>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
groupWhat the flights are grouped in: a Motion presence group lets each one finish rising. | ComponentType<{ children?: ReactNode }> | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/flights.tsx
Room.Flightcontent · <div>
One emoji rising. `--lane` is its lane from 0, for a skin to place it; `named` says whether it is the first of its sender's burst, which is the one that carries the name.
Required scope
Room.Flight must be inside <Room.Flights>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: FlightRecord) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/flight.tsx
Room.DeviceGroupcontent · <div>
The devices of one kind as a choice of one, for a menu's radio group: its `value` is the device in use and choosing another switches to it, or remembers it for when the camera or microphone is next turned on. Items are `Room.Devices` with `Room.DeviceItem` inside.
Required scope
Room.DeviceGroup must be inside <Room.Root>.
Props
3 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
kindRequired | "audioinput" | "videoinput" | — |
childrenContent rendered inside this part. | ReactNode | ((state: { chosen: string | null }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/device-group.tsx
Room.NoDevicesgate
The browser admits to no device of this kind yet — usually because nobody has been asked for permission, which is when a list is empty rather than when there is nothing plugged in.
Required scope
Room.NoDevices must be inside <Room.DeviceGroup>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/no-devices.tsx
Room.Devicesscope
Every device the picker around this is offering, each in its own scope.
Required scope
Room.Devices must be inside <Room.DevicePicker>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/devices.tsx
Room.DeviceItemcontent · <div>
One device in a `Room.DeviceGroup`, named as the browser names it.
Required scope
Room.DeviceItem must be inside <Room.Devices>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/device-item.tsx
Room.DevicePickerinput · <select>
Which microphone or camera to use. Choosing one while it is off remembers the choice, and the device is opened when the toggle beside it is turned on — so picking from a list never switches a light on by itself. The remembering is `useLocalMedia`'s, because that is what owns `set`. A list of one is not a choice, and a list of none is a browser that has not been asked for permission yet — neither is worth a control, so neither draws one.
Required scope
Room.DevicePicker must be inside <Room.Root>.
Props
3 documented props · Native <select> attributes supported
| Prop | Type | Default |
|---|---|---|
kindRequired | "audioinput" | "videoinput" | — |
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/device-picker.tsx
Room.DeviceOptioncontent · <option>
One device in the list. The label is empty until permission has been given once — that is the browser keeping the list from being a fingerprint, not a bug — so before permission this reads "Microphone 1" and after it reads the real name. → hooks/realtime/use-devices.ts
Required scope
Room.DeviceOption must be inside <Room.Devices>.
Props
2 documented props · Native <option> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/device-option.tsx
Room.ChosenDevicecontent · <span>
The name of the camera or microphone in use, for a menu's trigger: what a person checks before a call, and cannot see on a button that only says "Microphone". Its children stand in until the browser names a device, which it does only once one was allowed.
Required scope
Room.ChosenDevice must be inside <Room.Root> or <Join.Root>.
Props
3 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
kindRequired | "audioinput" | "videoinput" | — |
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/chosen-device.tsx
Room.MyLevelcontent · <span>
Your own microphone's level as `--level`, for a meter beside the microphone button: the one way to know a microphone is working before anybody has to ask whether you can be heard.
Required scope
Room.MyLevel must be inside <Room.Root>.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/my-level.tsx
Room.MediaToggleaction · <button>
Turns one kind of track on or off. `data-active` while it is on. Disabled while there is no room to send into, because there is nothing for a camera to reach and the light would come on anyway: a control bar that still worked over a room that was full or refused was the loudest thing wrong with a meeting that had failed. → docs/rules/capability-honesty.md
Required scope
Room.MediaToggle must be inside <Room.Root>.
Props
3 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
kindRequired | "audio" | "video" | "screen" | — |
childrenContent rendered inside this part. | ReactNode | ((state: { on: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/media-toggle.tsx
Room.MediaDeniedgate
The browser refused the camera or the microphone. `unsupported` is told apart from `denied` because the remedies are different and only one of them exists: a permission can be changed in the address bar, and a browser without WebRTC cannot be talked round. → docs/rules/capability-honesty.md
Required scope
Room.MediaDenied must be inside <Room.Root>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { unsupported: boolean }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/media-denied.tsx
Room.MutedNudgegate
You are talking and nobody can hear you: where a call says so, with a way to unmute. Rendering it is what starts the listening — on this device only, and only where the microphone was already allowed. → hooks/realtime/use-muted-nudge.ts
Required scope
Room.MutedNudge must be inside <Room.Root>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
group | GateGroupComponent | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/muted-nudge.tsx
Room.FullScreenaction · <button>
Fill the screen with the call, or leave full screen. Nothing where the browser cannot — an iPhone's Safari, a frame without permission — rather than a button that does nothing.
Required scope
Room.FullScreen must be inside <Room.CallFrame>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { on: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/full-screen.tsx
Room.PictureInPictureaction · <button>
Floats the video worth watching — a shared screen, then whoever is speaking — over other windows, and brings it back. `aria-pressed` while it floats; nothing in a browser that cannot.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { on: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/picture-in-picture.tsx
Room.ShowShortcutsaction · <button>
Open the sheet of keyboard shortcuts, for somebody who did not know to press `?`.
Required scope
Room.ShowShortcuts must be inside <Room.CallFrame>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/show-shortcuts.tsx
Room.ShortcutsSheetcontent
The sheet that lists the call's keys, as a dialog root: `open` and `onOpenChange` are handed to whatever it renders, so a skin's dialog opens on `?` and closes on Escape.
Required scope
Room.ShortcutsSheet must be inside <Room.CallFrame>.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/shortcuts-sheet.tsx
Room.Rootscope
The room everything under here is in. Joining happens in the provider's effect rather than here, so a component that renders twice before it commits does not put two peers in the roster for one tab. A root inside a root for the same room shares its connection, and its follow too: a presence list placed on a canvas has to move the canvas when somebody on it is picked, and two follows would be two lists of whom somebody is following. A root inside a root for the same room needs no service of its own, and says nothing when there is none. Optional with no service, or off, there is no room: its status is "off", so every gate stays shut and `Room.Present` hides what the room is drawn inside, and its sink is switched off, so a Composer inside renders nothing. → ../hooks/use-realtime-scope.ts
Props
8 documented props
| Prop | Type | Default |
|---|---|---|
configThe realtime config, a slug from the registry ( | string | — |
enabledLeft out or | Enabled | — |
roomRequired | string | — |
profileWho this browser shows itself as. Without one, a visitor gets an animal of their own; and somebody signed in is their session's name and picture whatever this says. | PeerProfile | — |
perPersonOne person across this browser's tabs: every tab takes the same name, the one a screen that counts people shows once. Off, each tab is somebody of its own. | boolean | — |
mediaOff, the transport never asks for a camera or a microphone. | boolean | — |
captionsCaptions start shown; each person can still hide them for themselves. | boolean | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/realtime/src/headless/room-root.tsx
When something is missing
- No service configuration
- Install the realtime setup below and import "@/services". Required service roots report missing setup; gates do not replace that configuration.
- Missing capability or parent scope
- Follow each part’s context requirements and the example composition. Capability-aware controls hide when the provider cannot perform their action.