Presence, Cursors, and Live Collaboration State
Sync multi-user presence and shared state with channels and optimistic local merges.
What Is Presence in Collaborative Apps?
Presence is the real-time awareness of who else is in the same space as you — and what they are doing. Think of Google Docs showing avatars at the top, or Figma rendering other users' cursors on the canvas.
- Presence state: which users are currently connected to a channel
- Cursor state: where each user's mouse or caret is positioned
- Awareness metadata: name, avatar, active selection, scroll position
In Next.js 15 with the App Router, you build real-time collaboration on top of a channel abstraction — typically provided by Supabase Realtime, Ably, Pusher, or a custom WebSocket server. The principles are the same across providers.
The key design goal is low-latency local feedback: your own cursor must move instantly, and remote cursors should follow with minimal delay.
Setting Up a Supabase Realtime Channel
Supabase Realtime channels support Presence natively. A channel is a named pub/sub room where clients can track each other's state. You create a channel once per collaborative session and call channel.subscribe() to join it.
Install the client and create a typed channel in a client component:
'use client'
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
export function useCollabChannel(roomId: string) {
// Each room gets its own channel scoped by ID
const channel = supabase.channel(`room:${roomId}`, {
config: {
presence: { key: crypto.randomUUID() }, // unique key per tab
},
})
return channel
}All lessons in this course
- Server-Sent Events from Route Handlers
- Integrating WebSocket Services in a Serverless World
- Streaming AI Responses Token-by-Token
- Presence, Cursors, and Live Collaboration State