Skip to content
SLICES / TECHNICAL FOLIO Nº 001FULL-STACK ASSEMBLIES — OPEN SOURCE

Headless / Realtime

Join a room and see who is here

Build room presence with the room's shared state and capability-aware controls.

37 public parts · 3examples

@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.

Behavior / inputGateGenericRenderer · not headlessScope
COMPONENT ANATOMYHover · focus · tap
ONE POSSIBLE DESIGNStatic preview

Join.Root · Scope

Join.RootRoot holds the preview tracks and the chosen name, and hands them to the room. Toggle kind="camera" | "microphone" switches the preview. Level writes the preview microphone's level as --level for a meter; @slices.sh/voice-canvas can draw the same signal as bars. Name is the name field, prefilled from auth when it is configured. Inside counts who is in the room and Faces shows them. Submit joins.

Installation

Choose what your component needs. Run these commands in an initialized Next.js app.

0

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 nova

Or start fresh: the slices CLI creates the app and sets it up.

npx slices.sh install
1

Install 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 services
2

Install the headless library

npm

Add the React parts to your app, then compose them with your own elements.

npm install @slices.sh/realtime@^0.1.6

Import from @slices.sh/realtime/headless.

3

Install the service

Connect the parts to a provider. Skip this step if this service is already configured in your app.

Provider
npx add realtime.p2p-mesh

Adds 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

    Only for “A room conversation”: npm install @slices.sh/composer@^0.1.4

    Start small

    Parts bring the behavior. Use their default elements, or supply your own with render.

    example.tsx
    "use client"// services/index.ts comes from https://slices.sh/r/services.json (or any provider setup)import "@/services"import { Room } from "@slices.sh/realtime/headless"export function Example() {  return (    <Room.Root room="lobby">      <Room.Peers>        <Room.Peer>          <Room.PeerAvatar />          <Room.PeerName />        </Room.Peer>      </Room.Peers>      <Room.Reconnecting>Reconnecting…</Room.Reconnecting>    </Room.Root>  )}

    People in the same room see each other. The provider handles presence and reconnection.

    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.

    Join.Rootscope

    Root holds the preview tracks and the chosen name, and hands them to the room. Toggle kind="camera" | "microphone" switches the preview. Level writes the preview microphone's level as --level for a meter; @slices.sh/voice-canvas can draw the same signal as bars. Name is the name field, prefilled from auth when it is configured. Inside counts who is in the room and Faces shows them. Submit joins.

    Props

    5 documented props

    Join.Root props
    PropTypeDefault
    config

    The realtime config, a slug from the registry (services/realtime/<config>.config.ts). The nearest root's, else "default", when left out.

    string—
    enabled

    Left out or true: required, and a missing service throws. "optional": renders its children, its parts render nothing, and its sink is switched off. false: off. A capability name ("relay") requires the service and that capability.

    Enabled—
    roomRequired
    string—
    onJoined

    Once somebody has gone in: where a page sends them to the room, when it is elsewhere.

    () => void—
    children

    Content 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/join-root.tsx

    Join.Waitinggate

    Still at the door.

    Required scope

    Join.Waiting must be inside <Join.Root>.

    Props

    1 documented props

    Join.Waiting props
    PropTypeDefault
    children

    Content 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/join-waiting.tsx

    Join.Nameinput · <input>

    The name you go in under. Left empty, you go in as the animal its placeholder shows.

    Required scope

    Join.Name must be inside <Join.Root>.

    Props

    2 documented props · Native <input> attributes supported

    Join.Name props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/join-name.tsx

    Join.Toggleaction · <button>

    The camera or the microphone, at the door. `data-active` while it is on. Its own part rather than `Room.MediaToggle`, which is disabled until there is a room to send into: here there never is one, and the whole point is to try the camera before there is.

    Required scope

    Join.Toggle must be inside <Join.Root>.

    Props

    3 documented props · Native <button> attributes supported

    Join.Toggle props
    PropTypeDefault
    kindRequired
    "audio" | "video"—
    children

    Content rendered inside this part.

    ReactNode | ((state: { on: boolean }) => ReactNode)—
    render

    Replace 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/join-toggle.tsx

    Join.Levelcontent · <span>

    Your microphone at the door, as `--level` from 0 to 1: say something and watch it move.

    Required scope

    Join.Level must be inside <Join.Root>.

    Props

    2 documented props · Native <span> attributes supported

    Join.Level props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/join-level.tsx

    Join.Insidegate

    Who is already in, in words: "Amara and Ben are in this call". Nothing where the room could not be seen into, because saying "nobody is here" about a room nobody could look at would be a guess dressed as a fact. → docs/rules/capability-honesty.md

    Required scope

    Join.Inside must be inside <Join.Root>.

    Props

    1 documented props

    Join.Inside props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: Inside) => ReactNode)—

    Required props are marked. A dash means no explicit default is declared in this component.

    Source: @slices.sh/realtime/src/headless/join-inside.tsx

    Join.Facesscope

    The faces of whoever is inside, each in a peer's scope, so `Room.PeerAvatar` and `Room.PeerName` draw them as they would in the room.

    Required scope

    Join.Faces must be inside <Join.Root>.

    Props

    3 documented props

    Join.Faces props
    PropTypeDefault
    max

    The most faces to show; the words beside them say how many more.

    number4
    group
    ComponentType<{ children?: ReactNode }>—
    children

    Content 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/join-faces.tsx

    Join.Submitaction · <button>

    Goes in, with the camera, microphone and name chosen here. `data-pending` once pressed, while the page the room is on loads.

    Required scope

    Join.Submit must be inside <Join.Root>.

    Props

    2 documented props · Native <button> attributes supported

    Join.Submit props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { joined: boolean }) => ReactNode)—
    render

    Replace 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/join-submit.tsx

    Join.Joinedgate

    Through the door: where the room itself goes, when it is on the same page.

    Required scope

    Join.Joined must be inside <Join.Root>.

    Props

    1 documented props

    Join.Joined props
    PropTypeDefault
    children

    Content 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/join-joined.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

    Room.Root props
    PropTypeDefault
    config

    The realtime config, a slug from the registry (services/realtime/<config>.config.ts). The nearest root's, else "default", when left out.

    string—
    enabled

    Left out or true: required, and a missing service throws. "optional": renders its children, its parts render nothing, and its sink is switched off. false: off. A capability name ("relay") requires the service and that capability.

    Enabled—
    roomRequired
    string—
    profile

    Who 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—
    perPerson

    One 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—
    media

    Off, the transport never asks for a camera or a microphone.

    boolean—
    captions

    Captions start shown; each person can still hide them for themselves.

    boolean—
    children

    Content 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

    Room.Joininggate

    Still finding the room.

    Required scope

    Room.Joining must be inside <Room.Root>.

    Props

    1 documented props

    Room.Joining props
    PropTypeDefault
    children

    Content 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-states.tsx

    Room.Failedgate

    The room could not be joined, for a reason nothing here can mend. `denied` is told apart from the rest because the remedies are opposite: one is a permission, the other is a retry. A full room is not here at all — it is `Room.Full`, because it is the one refusal that mends itself when somebody leaves. A server that lacks configuration is not here either: it is `Room.MissingConfiguration`, which says so in words for somebody to report, instead of this one's "could not be reached" beside it.

    Required scope

    Room.Failed must be inside <Room.Root>.

    Props

    1 documented props

    Room.Failed props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { denied: boolean }) => ReactNode)—

    Required props are marked. A dash means no explicit default is declared in this component.

    Source: @slices.sh/realtime/src/headless/room-failed.tsx

    Room.MissingConfigurationgate

    The server that holds the rooms lacks a variable it needs, such as its room table. `Configuration.Missing` for this family, narrowed to the room service: a call that also transcribes could have a transcription key missing, and that is the captions' notice, not a room's. It stands where `Room.Failed` would, and works outside a root as well, because a door has learned it from its first look at the room before anybody has joined anything.

    Props

    1 documented props

    Room.MissingConfiguration props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { scopes: readonly string[] }) => ReactNode)—

    Required props are marked. A dash means no explicit default is declared in this component.

    Source: @slices.sh/realtime/src/headless/room-missing-configuration.tsx

    Room.Fullgate

    The room is full. Its own gate rather than a kind of failure, because it is the one refusal that is nobody's fault and fixes itself: a mesh holds as many people as `services/realtime/config.ts` says it holds, and the next person to leave makes room. The transport keeps retrying, so what belongs here is a sentence and a way to try now — never an apology for a broken app.

    Required scope

    Room.Full must be inside <Room.Root>.

    Props

    1 documented props

    Room.Full props
    PropTypeDefault
    children

    Content 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-full.tsx

    Room.Leftgate

    This browser closed the room on purpose. The service calls it `idle`, which is also what it calls a room nothing has been tried on yet, and the two owe a person opposite screens: one is a spinner, and this one is a door back in. Without it a meeting somebody left became an empty grid with a live control bar.

    Required scope

    Room.Left must be inside <Room.Root>.

    Props

    1 documented props

    Room.Left props
    PropTypeDefault
    children

    Content 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-left.tsx

    Room.Rejoinaction · <button>

    Take the room again. Nothing at all when the room cannot be reopened — one handed in from outside is somebody else's to reopen — because a button that cannot work is worse than no button. → docs/rules/capability-honesty.md It takes the focus when it appears into a page whose focus has fallen to nothing, which is what pressing Leave does: the button that had the focus is gone with the rest of the room, and somebody on a keyboard was left starting again from the top of the page. (WCAG 2.4.3)

    Required scope

    Room.Rejoin must be inside <Room.Root>.

    Props

    2 documented props · Native <button> attributes supported

    Room.Rejoin props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/rejoin.tsx

    Room.Presentgate

    In the room, or on the way in: everything but having left it or been refused it. What a room is drawn inside — its tiles, its controls, what it says about a camera — so that leaving takes all of it away and `Room.Left` has the screen to itself. Without it the grid and the control bar stayed up behind "You left the meeting", still showing everybody who was in it. Being refused (unreachable, denied, full: a full room is a failed status) takes the room away the same way, and so does no service to join (an optional root without one), so `Room.Failed` and `Room.Full` are not drawn under a "Live" header and controls that reach nobody.

    Required scope

    Room.Present must be inside <Room.Root>.

    Props

    1 documented props

    Room.Present props
    PropTypeDefault
    children

    Content 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-present.tsx

    Room.Reconnectinggate

    The link dropped and is being rebuilt. Its own state rather than a kind of failure, because nothing is lost and nobody has to do anything: a banner offering "Try again" here would be offering what is already happening.

    Required scope

    Room.Reconnecting must be inside <Room.Root>.

    Props

    1 documented props

    Room.Reconnecting props
    PropTypeDefault
    children

    Content 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/reconnecting.tsx

    Room.Connectedgate

    In the room, with a live signalling path.

    Required scope

    Room.Connected must be inside <Room.Root>.

    Props

    1 documented props

    Room.Connected props
    PropTypeDefault
    children

    Content 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/connected.tsx

    Room.Rosterscope

    A list of people somebody can search, for a room too big to scan.

    Props

    1 documented props

    Room.Roster props
    PropTypeDefault
    children

    Content 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/roster.tsx

    Room.RosterSearchinput · <input>

    Narrows the roster around it to the names that contain what is typed.

    Required scope

    Room.RosterSearch must be inside <Room.Roster>.

    Props

    2 documented props · Native <input> attributes supported

    Room.RosterSearch props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/roster-search.tsx

    Room.NoMatchesgate

    Somebody searched the roster and nobody's name has that in it.

    Required scope

    Room.NoMatches must be inside <Room.Roster>.

    Room.NoMatches must be inside <Room.Root>.

    Props

    1 documented props

    Room.NoMatches props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { query: string }) => ReactNode)—

    Required props are marked. A dash means no explicit default is declared in this component.

    Source: @slices.sh/realtime/src/headless/no-matches.tsx

    Room.Peersscope

    Who is here, each in their own scope: you first, then everybody in the order they came. Peers are connections, so two tabs are two of them. Collapsing per person is a display decision and lives here rather than in the wire format, which is why it is a prop. Inside a `Room.Roster`, only the names matching its search are listed.

    Required scope

    Room.Peers must be inside <Room.Root>.

    Props

    7 documented props

    Room.Peers props
    PropTypeDefault
    perPerson

    One row per person rather than per connection: two tabs become one entry.

    booleanfalse
    includeMe

    Whether you are in the list. A face pile usually wants you out of it.

    booleantrue
    max

    The most items a pile may take, counting its "+N". Past it the pile shows one face fewer and Room.MorePeers takes the last place, so the row keeps the width it was given.

    number—
    overflowOf

    Only those a pile of this size leaves out: what its "+N" opens.

    number—
    limit

    @deprecated A hard cut with no room kept for the count; use max.

    number—
    group

    What the items are grouped in. A Motion presence group here is what lets a face leave rather than vanish, without this part knowing anything about animation.

    ComponentType<{ children?: ReactNode }>—
    children

    Content 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/peers.tsx

    Room.Peercontent · <li>

    One peer's row or tile. `data-active` while the connection is actually live.

    Required scope

    Room.Peer must be inside <Room.Peers>.

    Props

    2 documented props · Native <li> attributes supported

    Room.Peer props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { connected: boolean }) => ReactNode)—
    render

    Replace 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/peer.tsx

    Room.PeerAvatarcontent · <span>

    A peer's face: their picture once it has loaded, their initials until then and for good if it never does, with their colour as `--peer` and the ink that reads on it as `--peer-ink`, for the skin to paint with. → ./peer-look.ts The picture is painted as the face's background and the initials are dropped when it arrives, so a skin's size, shape and colour work unchanged and a slow or dead address costs nothing but the picture. → ./use-peer-picture.ts Named after the person rather than after the two letters standing in for them: a pile of these is the whole of a presence list, and without a name it reads out as "AB CD EF". The name is the peer's own and is never a word a skin or a copy chooses, so it is locked here rather than asked for. (WCAG 1.1.1)

    Required scope

    Room.PeerAvatar must be inside <Room.Peers>.

    Props

    2 documented props · Native <span> attributes supported

    Room.PeerAvatar props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/peer-avatar.tsx

    Room.PeerNamecontent · <span>

    Peers repeats the people here, up to max, and Peer is one scope. PeerName, PeerAvatar (with --peer colour), PeerStatus (the data path, in words), PeerActivity (here, idle, away) describe the peer in scope. PeerCount counts them. IsMe, NotMe and Me pick out you. MorePeers gives { hidden, names } for the ones a pile leaves out. Unreachable renders for a peer this browser has no route to, which needs a relay. Roster, RosterSearch and NoMatches make a searchable list for a big room.

    Required scope

    Room.PeerName must be inside <Room.Peers>.

    Props

    2 documented props · Native <span> attributes supported

    Room.PeerName props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/peer-name.tsx

    Room.PeerStatuscontent · <span>

    Whether this peer's data path is open, in words. The dot beside an avatar carries the most interesting fact on the screen — recorded in the roster, or actually reachable — and carries it in a colour, which is no use to anybody who cannot see it. Usually rendered visually hidden beside the dot. (WCAG 1.4.1) `reachable` is `null` while the link is still being made, which is a third thing and not a shade of the other two: for a second or two after somebody joins, nobody knows yet. → docs/rules/capability-honesty.md

    Required scope

    Room.PeerStatus must be inside <Room.Peers>.

    Props

    2 documented props · Native <span> attributes supported

    Room.PeerStatus props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { reachable: boolean | null }) => ReactNode)—
    render

    Replace 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/peer-status.tsx

    Room.PeerActivitycontent · <span>

    Where a peer's attention is — here, idle, away — and what they say they are doing. One part for the dot and the words both, because the dot is the words in a colour: a skin draws the dot from `data-state` and a copy says it from the state, so the two can never disagree. `data-state` is absent while the peer has said nothing, which draws no dot rather than a green one nobody earned. → docs/rules/capability-honesty.md

    Required scope

    Room.PeerActivity must be inside <Room.Peers>.

    Room.PeerActivity must be inside <Room.Root>.

    Props

    2 documented props · Native <span> attributes supported

    Room.PeerActivity props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: ActivityState) => ReactNode)—
    render

    Replace 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/peer-activity.tsx

    Room.NotMegate

    The person in scope is somebody else — where a control about them makes no sense for you.

    Required scope

    Room.NotMe must be inside <Room.Peers> or <Room.Seats>.

    Room.NotMe must be inside <Room.Root>.

    Props

    1 documented props

    Room.NotMe props
    PropTypeDefault
    children

    Content 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/not-me.tsx

    Room.Unreachablegate

    The room names this peer and this browser has no route to them. Told apart from a camera that is off, which looks identical and means the opposite: one is a choice somebody made and the other is a network that needs a relay. A mesh over STUN alone cannot reach a peer behind a symmetric NAT at all, and the only honest thing a tile can do is say so. → resources/turn/README.md Also told apart from a link that is still being made, which looks identical again and is what every peer's first second in the room looks like: a badge that appeared the instant somebody joined and went away when their channel opened would accuse the network of something ordinary, and be ignored by the time it was true. → hooks/realtime/use-link-settled.ts

    Required scope

    Room.Unreachable must be inside <Room.Peers>.

    Props

    1 documented props

    Room.Unreachable props
    PropTypeDefault
    children

    Content 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/unreachable.tsx

    Room.IsMegate

    The peer in scope is you — or your other tab, which is you too.

    Required scope

    Room.IsMe must be inside <Room.Peers>.

    Room.IsMe must be inside <Room.Root>.

    Props

    1 documented props

    Room.IsMe props
    PropTypeDefault
    children

    Content 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/is-me.tsx

    Room.Megate

    You, in the scope of a peer: for the one place on a screen that is yours alone, beside the faces of everybody else rather than first among them. Nothing until you have joined.

    Required scope

    Room.Me must be inside <Room.Root>.

    Props

    1 documented props

    Room.Me props
    PropTypeDefault
    children

    Content 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/me.tsx

    Room.PeerCountcontent · <span>

    How many are here and reachable, counting you. → per-person.ts `reachableIn`

    Required scope

    Room.PeerCount must be inside <Room.Root>.

    Props

    3 documented props · Native <span> attributes supported

    Room.PeerCount props
    PropTypeDefault
    perPerson

    People rather than connections, so somebody in two tabs is counted once.

    booleanfalse
    children

    Content rendered inside this part.

    ReactNode | ((state: { count: number }) => ReactNode)—
    render

    Replace 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/peer-count.tsx

    Room.MorePeerscontent · <span>

    The people a pile leaves out: how many, and who. Who as well as how many, because a "+5" nobody can expand is a number. The names reach the content function, so a screen can say them to a screen reader and open them in a list. Nothing at all when everybody fits.

    Required scope

    Room.MorePeers must be inside <Room.Root>.

    Props

    6 documented props · Native <span> attributes supported

    Room.MorePeers props
    PropTypeDefault
    max

    The same number the pile beside it was given as its max.

    number—
    limit

    @deprecated The pile's old hard cut; use max.

    number—
    perPerson
    booleanfalse
    includeMe
    booleantrue
    children

    Content rendered inside this part.

    ReactNode | ((state: MorePeersState) => ReactNode)—
    render

    Replace 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/more-peers.tsx

    Room.Alonegate

    You are in the room and nobody else is: the moment a screen can say what it needs a second connection for. Another tab of yours is somebody here, which is how a person alone tries a room out. Somebody the roster names that nothing reaches is not here, as Room.PeerCount does not count them. Not while joining or failed, when nobody being here is not yet, or not ever, the story.

    Required scope

    Room.Alone must be inside <Room.Root>.

    Props

    1 documented props

    Room.Alone props
    PropTypeDefault
    children

    Content 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/alone.tsx

    Room.Announcercontent · <p>

    What just happened in the room, for somebody who cannot see it happen. A polite live region, always in the DOM and usually empty: a region that appears with its first sentence is a region a screen reader was not watching when the sentence arrived. The words are the pattern's, because they are words. → docs/rules/words-in-copy-looks-in-skin.md

    Props

    2 documented props · Native <p> attributes supported

    Room.Announcer props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode | ((state: { news: RoomNews | null }) => ReactNode)—
    render

    Replace 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/announcer.tsx

    Room.Leaveaction · <button>

    Close the room. Nothing to do once it is closed, which is what `Room.Rejoin` is for.

    Required scope

    Room.Leave must be inside <Room.Root>.

    Props

    2 documented props · Native <button> attributes supported

    Room.Leave props
    PropTypeDefault
    children

    Content rendered inside this part.

    ReactNode—
    render

    Replace 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/leave.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.
    Read the setup guide →