Presence Tracking

Know who's online in real-time. Track user presence across your project and handle join/leave events.

What is Presence?

Presence tracking allows you to see which actors (users, devices, or servers) are currently connected to your project. This is essential for features like:

  • Online/offline indicators in chat apps
  • Showing who's viewing a document
  • Live user counts
  • Typing indicators
Client-level: Presence events (presence:join, presence:leave, presence:update) are tracked at the client level, not room-scoped. Use client.on() to listen for these events. For observing presence across rooms, use Lobbies.

Setting Presence

Set your presence after connecting. Presence data can include any custom fields you need:

import { NoLag } from '@nolag/js-sdk'

const client = NoLag('your_access_token')
await client.connect()

// Set presence at the client level
client.setPresence({
  username: 'Alice',
  status: 'online',
  avatar: 'https://example.com/alice.jpg'
})

// Presence events are client-level, not room-scoped
client.on('presence:join', (actor) => {
  console.log(`${actor.presence.username} joined`)
})

client.on('presence:leave', (actor) => {
  console.log(`${actor.presence.username} left`)
})

client.on('presence:update', (actor) => {
  console.log(`${actor.presence.username} updated status to ${actor.presence.status}`)
})

Presence Events

Listen for these events to track when actors come online, go offline, or update their status:

  • presence:join - An actor connected and set their presence
  • presence:leave - An actor disconnected
  • presence:update - An actor updated their presence data

Getting Current Presence

Get a list of all actors currently present. Note that getPresence() returns all actors from the local cache (not room-scoped):

// Get all present actors from local cache (client-level)
const actors = client.getPresence()
console.log('Users online:', actors.length)

actors.forEach((actor) => {
  console.log(`- ${actor.presence.username} (${actor.actorTokenId})`)
})

// Fetch fresh presence list from server
const freshList = await client.fetchPresence()
console.log('Server says online:', freshList.length)

Local Cache vs Server Fetch

  • JavaScript: client.getPresence() returns all, client.getPresence(actorId) returns one, client.fetchPresence() fetches from server
  • Python: client.get_all_presence() returns all, client.get_presence(actor_token_id) returns one
  • Go: client.GetPresence(topic) fetches presence for a specific topic from the server

The local cache is automatically updated when you receive presence events.

Updating Presence

Update your presence data at any time by calling setPresence() again:

// Update your presence data (e.g., status change)
client.setPresence({
  username: 'Alice',
  status: 'away',
  lastActive: Date.now()
})

// Client-level presence is automatically re-sent on reconnect

Typing Indicators

A common use case is showing typing indicators. Use presence updates with a debounce:

// For typing indicators, update presence with typing status
let typingTimeout: NodeJS.Timeout

function sendTyping() {
  client.setPresence({
    username: 'Alice',
    status: 'online',
    isTyping: true
  })

  // Clear typing after 3 seconds of inactivity
  clearTimeout(typingTimeout)
  typingTimeout = setTimeout(() => {
    client.setPresence({
      username: 'Alice',
      status: 'online',
      isTyping: false
    })
  }, 3000)
}

// Listen for typing from others (client-level event)
client.on('presence:update', (actor) => {
  if (actor.presence.isTyping) {
    showTypingIndicator(actor.presence.username)
  } else {
    hideTypingIndicator(actor.presence.username)
  }
})

Actor Presence Structure

Each actor presence object contains:

interface ActorPresence {
  actorTokenId: string    // Unique actor identifier
  actorType: 'device' | 'user' | 'server'
  presence: {             // Your custom presence data
    username?: string
    status?: string
    // ... any other fields you set
  }
  joinedAt?: number       // Timestamp when actor connected
}

Best Practices

  • Keep presence data small - Only include necessary information (username, status, avatar URL)
  • Use debouncing - For typing indicators, debounce updates to avoid flooding
  • Handle reconnections - Client-level presence is automatically re-sent on reconnect
  • Consider privacy - Let users opt out of presence tracking if needed
  • Use fetchPresence() sparingly - The local cache is usually sufficient

Next Steps