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
Section titled “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
Section titled “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
Section titled “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
Section titled “Update presence state”Call track() again at any time to update your state — all subscribers receive a presence:update event:
// Update cursor position on mousemovedocument.addEventListener('mousemove', ({ clientX, clientY }) => { channel.track({ ...myState, cursor: { x: clientX, y: clientY } })})Untrack (go offline)
Section titled “Untrack (go offline)”channel.untrack()// Broadcasts presence:leave to all subscribers immediatelyComplete presence example
Section titled “Complete presence example”import { useAerostack } from '@aerostack/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> )}See the Live Presence example for a complete, interactive demo with an event log.