Presence
Track who is online on a channel, what they’re doing, and when they leave. Aerostack automatically cleans up presence state when a user disconnects — no server code required.
How it works
Call channel.track(state) with any JSON object describing the user. All other subscribers on the same channel receive presence:join, presence:update, and presence:leave events.
When a user disconnects, Aerostack waits 90 seconds (to allow reconnection), then broadcasts presence:leave for all their tracked channels.
Track presence
const channel = realtime.channel('canvas/room-1')
channel.track({
userId: user.id,
name: user.name,
color: '#FF5733',
cursor: { x: 120, y: 340 },
})
channel.subscribe()Listen for presence events
channel
.on('presence:join', ({ data }) => {
// data: { userId, name, color, cursor, ... }
setPeers(prev => [...prev, data])
})
.on('presence:update', ({ data }) => {
setPeers(prev => prev.map(p => p.userId === data.userId ? data : p))
})
.on('presence:leave', ({ data }) => {
setPeers(prev => prev.filter(p => p.userId !== data.userId))
})Update presence state
Call track() again at any time to update your state — all subscribers receive a presence:update event:
// Update cursor position on mousemove
document.addEventListener('mousemove', ({ clientX, clientY }) => {
channel.track({ ...myState, cursor: { x: clientX, y: clientY } })
})Untrack (go offline)
channel.untrack()
// Broadcasts presence:leave to all subscribers immediatelyComplete presence example
import { useAerostack } from '@aerostack/react'
import { useEffect, useState, useRef } from 'react'
const COLORS = ['#FF5733', '#3498DB', '#2ECC71', '#F39C12', '#9B59B6']
function LivePresence({ roomId, userId, userName }) {
const { realtime } = useAerostack()
const [peers, setPeers] = useState([])
const color = useRef(COLORS[Math.floor(Math.random() * COLORS.length)])
useEffect(() => {
const channel = realtime.channel(`room/${roomId}`)
channel
.on('presence:join', ({ data }) => {
setPeers(prev => {
if (prev.find(p => p.userId === data.userId)) return prev
return [...prev, data]
})
})
.on('presence:update', ({ data }) => {
setPeers(prev => prev.map(p => p.userId === data.userId ? { ...p, ...data } : p))
})
.on('presence:leave', ({ data }) => {
setPeers(prev => prev.filter(p => p.userId !== data.userId))
})
.subscribe()
// Announce your presence
channel.track({ userId, name: userName, color: color.current, status: 'online' })
return () => {
channel.untrack()
channel.unsubscribe()
}
}, [realtime, roomId, userId, userName])
return (
<div className="flex gap-2 flex-wrap">
{peers.map(peer => (
<div
key={peer.userId}
className="flex items-center gap-2 px-3 py-1.5 rounded-full text-white text-sm"
style={{ background: peer.color }}
>
<span className="w-2 h-2 rounded-full bg-white/80" />
{peer.name}
</div>
))}
</div>
)
}Presence state is not persisted — it exists only in memory. When the server restarts, all presence is cleared.
See the Live Presence example for a complete, interactive demo with an event log.