Realtime presence

Who is here. Without asking your API.

Subscribe to a presence channel and the current roster arrives with the subscription, so the room renders populated instead of empty. After that, every client hears the same joins and leaves. Your backend assigns each member their identity when it signs the subscription, which is what makes the list trustworthy.

room.ts
18 members
const room = bird.subscribe("presence-room-42");

room.bind("bird:subscription_succeeded", () => {
  // The roster is already here. Paint it before anyone moves.
  render({ me: room.myId, members: [...room.members.values()] });
});

room.bind("bird:member_added", (member) => join(member.member_id));
room.bind("bird:member_removed", (member) => leave(member.member_id));

input.addEventListener("input", () => {
  room.trigger("client-typing", { at: Date.now() });
});

A member is a person, not a socket.

Three tabs are one member.

Presence is part of the Bird Realtime API, and it counts identities rather than sessions. A member joins the list when their first connection subscribes and leaves it when their last one goes; the tabs in between produce nothing, so a roster does not flicker every time somebody opens a duplicate window. If you need the socket count instead, that is a separate app setting and a separate event.

What presence gives you

The roster, the changes, and the identity to hang both on. All of it signed by your own backend.

  1. 01

    The roster, on arrival.

    bird:subscription_succeeded carries the current member list, so the first paint is the real room. You apply joins and leaves on top of it instead of building the list from an empty state.

  2. 02

    An identity you control.

    Your authorization endpoint returns a member id and optional member info, and signs that exact string. A client can ask to subscribe but cannot choose who it is. Keep member info to small public profile data like a display name or an avatar: every member of the channel receives it, and it is capped at 1 KB.

  3. 03

    Reads from your server.

    List a channel's members, or ask for member_count without listing them, straight from the API. Neither needs a subscription, so a backend can answer who is in a room without joining it. Member count is presence-only; asking for it on a public or private channel is a validation error, because those channels have no members.

  4. 04

    Client events between peers.

    A presence channel is one of the two places a client can trigger an event directly to the others, so typing indicators and cursor positions never touch your API. Ten per second per connection, and the sender does not receive its own event.

  5. 05

    Connection counts, when you want them.

    Members answer who is here. Turn on connection counting and connection count events, and bird:connection_count answers how many sockets are open on the channel, which is the number the three-tab person contributes three to.

Member-addressed events

Sometimes the destination is a person, not a room.

A signed-in member can be addressed directly, with no channel to name and no subscription to authorize. One call reaches every connection that identity holds, across every tab and device, and nobody else, even if another member binds the same event name. If they have no live connection the call still succeeds: delivery is not queued, so store the notification and load it when they come back. The same identity is what disconnect targets when a session has to end now.

members.ts
server
// Address the person, not a channel. Every tab, every device.
await bird.realtime.members.send(APP_ID, "u_42", {
  event: "order.shipped",
  data: { order_id: "ord_123" },
});

// The roster, read from your server. No subscription needed.
const roster = await bird.realtime.channels.members(APP_ID, "presence-room-42");

// Password changed. Close every connection they hold.
await bird.realtime.members.disconnect(APP_ID, "u_42");

A buddy list is not a room.

Presence answers who is in this room with me. Watchlist events answer whether the people I care about are online anywhere. A member's signed identity can carry up to 100 member ids, and with watchlist events enabled the connection is told when any of them comes online or goes offline, with the whole list's current status delivered right after sign-in. Because the list lives inside the signed identity, your endpoint decides who may watch whom. No channel per relationship, and the people being watched do nothing beyond signing in.

Go deeper in the docs.

Presence channels covers the roster, member info, and the server-side reads. Authorizing channels is the contract your backend implements, sending events to a member compares member events with private channels, and watchlist events is the follow-list variant.

Render the room, not a loading state.

Presence, client events, and member-addressed delivery come with every Realtime app. The free plan covers 100 concurrent connections.

Starten Sie mit einem Kanal.
Fügen Sie die anderen hinzu, wenn Sie bereit sind.

Ein Test-API-Key steht Ihnen sofort zur Verfügung. Der Produktivzugang wird freigeschaltet, sobald Sie eine Zahlungsmethode hinzufügen und einen Absender verifizieren.

Sie nutzen Claude Code, Cursor oder Codex? Kopieren Sie einen Setup-Prompt und Ihr Agent installiert die Bird CLI und Skills für Sie. Wählen Sie Ihren:

Cursor