usePresence- derived online/offline status from heartbeats stored on the users collection. Persistent. ~60s granularity.usePresenceRoom- high-frequency ephemeral state (cursors, typing, viewport) broadcast through a dedicated Durable Object. In-memory only.
Online status
usePresence reads lastSeenAt from the users collection in the current RecordScope and sends a heartbeat every 60 seconds. A user is “online” if their last heartbeat was within timeoutMs (default 5 minutes).
usePresence for “currently online” lists where 60-second granularity is acceptable.
See usePresence for the full return shape and options.
Live cursors and typing
For sub-second cursor or typing indicators, useusePresenceRoom. It connects to a dedicated PresenceRoom Durable Object that holds peer state in memory only - nothing is persisted, and disconnecting removes you from peers on every other client within a second.
peers excludes the current user. Identity fields (userId, userName, etc.) come from the verified JWT - peers can’t spoof them. See usePresenceRoom for the full peer shape.
Scoping rooms
The first argument tousePresenceRoom is any string. Two clients passing the same scope ID connect to the same PresenceRoom instance and see each other.
State merging
updateState shallow-merges into your peer’s state object, so you can broadcast multiple kinds of state independently:
false or null - omitting it leaves the previous value intact.
Throttling cursor updates
onMouseMove fires far more often than you need to broadcast. Without throttling, you’ll flood the room with messages. A requestAnimationFrame gate works:
Disconnects and reconnects
The hook reconnects automatically. When the WebSocket drops,connected flips to false and your peer is removed from every other client’s peers list. On reconnect you re-join with empty state - you must re-send your cursor or typing flag if you want it visible again. Use connected to dim or hide your own optimistic state during the gap.
User colors
Cursor displays use a stable color per user.getUserColor.
Typing indicator
Cursor overlay
Choosing a primitive
| Primitive | Use when | Persistence |
|---|---|---|
usePresenceRoom | Cursors, typing, viewport pings, “active on this page” indicators | In-memory, ephemeral |
Yjs awareness (via useYjsField / useYjsRoom) | Editor cursors and selections that must stay in sync with CRDT state over the same socket | In-memory, ephemeral |
useGameRoom | Server-authoritative tick loops (player position, ready flags, game inputs) the server validates or simulates | Server-validated |
Next steps
- Canvas - real-time shapes and viewports on
useCanvas. - Collaborative editing - Yjs-backed text and rich data.
- Real-time sync - how sync works under the hood.
- Realtime reference - full hook signatures and return shapes.