Skip to content

Presence

Presence stores the current live state for a room member. Use it for online state, cursors, selections, typing indicators, active tools, viewport ranges, and other short-lived UI state.

Terminal window
app.post("/rooms/:roomId/join", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const roomId = pathSegments[2]
return await live.room(roomId).join({
presence: {
status: "online",
typing: false,
},
})
})

join() returns the member record:

Terminal window
{
room: "project_123",
kind: "room",
memberId: "user_123",
joinedAt: "2026-05-25T00:00:00.000Z",
lastSeenAt: "2026-05-25T00:00:00.000Z",
metadata: {},
presence: {
status: "online",
typing: false
}
}
Terminal window
app.post("/rooms/:roomId/presence", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const roomId = pathSegments[2]
const body = await request.json()
return await live.room(roomId).presence({
presence: {
cursor: body.cursor,
selection: body.selection,
typing: body.typing,
},
})
})

Presence values are JSON. Keep them small and focused on the live UI state a client needs to render.

Use the same presence() method for different live UI needs:

Terminal window
await room.presence({
presence: {
status: "online",
typing: true,
},
})
Terminal window
await room.presence({
presence: {
cursor: 128,
selection: { from: 120, to: 128 },
},
})
Terminal window
await room.presence({
presence: {
tool: "pen",
viewport: { x: 120, y: 80, zoom: 1.25 },
},
})
Terminal window
app.get("/rooms/:roomId/members", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const roomId = pathSegments[2]
const members = await live.room(roomId).members({
limit: 100,
})
return Response.json({ members })
})

Each member includes metadata, presence, actor, joined time, and last seen time.

Terminal window
app.post("/rooms/:roomId/leave", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const roomId = pathSegments[2]
return await live.room(roomId).leave()
})

Use leave() when a user closes a document, leaves a lobby, or disconnects cleanly.

By default, Realtime derives a member ID from the resolved actor. Pass memberId when you need a stable client or device key:

Terminal window
await room.join({
memberId: `device_${deviceId}`,
presence: {
status: "online",
},
})

This is useful when one user can have multiple tabs or devices in the same room.

Use presence for per-member live state and metadata for shared room state:

Terminal window
await room.metadata({
metadata: {
title: "Incident response",
mode: "war_room",
locked: false,
},
})
await room.presence({
presence: {
status: "online",
role: "incident_commander",
},
})

Clients can render shared room settings and member-specific state separately.