Realtime

Presence

Share state between users with Realtime Presence.


Let's explore how to implement Realtime Presence to track state between multiple users.

Usage#

You can use the Supabase client libraries to track Presence state between users.

How Presence works#

Presence lets each connected client publish a small piece of state—called a “presence payload”—to a shared channel. Supabase stores each client’s payload under a unique presence key and keeps a merged view of all connected clients.

When any client subscribes, disconnects, or updates their presence payload, Supabase triggers one of three events:

  • sync — the full presence state has been updated
  • join — a new client has started tracking presence
  • leave — a client has stopped tracking presence

The complete presence state returned by presenceState() looks like this:

1
{
2
"client_key_1": [{ "userId": 1, "typing": false }],
3
"client_key_2": [{ "userId": 2, "typing": true }]
4
}

Initialize the client#

Get the Project URL and key from the project's Connect dialog.

1
import { createClient } from '@supabase/supabase-js'
2
3
const SUPABASE_URL = 'https://<project>.supabase.co'
4
const SUPABASE_KEY = '<sb_publishable_... or anon key>'
5
6
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)

Sync and track state#

Listen to the sync, join, and leave events triggered whenever any client joins or leaves the channel or changes their slice of state:

1
import { createClient } from '@supabase/supabase-js'
2
const supabase = createClient('your_project_url', 'your_supabase_api_key')
3
4
// ---cut---
5
const roomOne = supabase.channel('room_01')
6
7
roomOne
8
.on('presence', { event: 'sync' }, () => {
9
const newState = roomOne.presenceState()
10
console.log('sync', newState)
11
})
12
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
13
console.log('join', key, newPresences)
14
})
15
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
16
console.log('leave', key, leftPresences)
17
})
18
.subscribe()

Sending state#

You can send state to all subscribers using track():

1
import { createClient } from '@supabase/supabase-js'
2
const supabase = createClient('your_project_url', 'your_supabase_api_key')
3
4
// ---cut---
5
const roomOne = supabase.channel('room_01')
6
7
const userStatus = {
8
user: 'user-1',
9
online_at: new Date().toISOString(),
10
}
11
12
roomOne.subscribe(async (status) => {
13
if (status !== 'SUBSCRIBED') { return }
14
15
const presenceTrackStatus = await roomOne.track(userStatus)
16
console.log(presenceTrackStatus)
17
})

A client will receive state from any other client that is subscribed to the same topic (in this case room_01). It will also automatically trigger its own sync and join event handlers.

Stop tracking#

You can stop tracking presence using the untrack() method. This will trigger the sync and leave event handlers.

1
import { createClient } from '@supabase/supabase-js'
2
const supabase = createClient('your_project_url', 'your_supabase_api_key')
3
const roomOne = supabase.channel('room_01')
4
5
// ---cut---
6
const untrackPresence = async () => {
7
const presenceUntrackStatus = await roomOne.untrack()
8
console.log(presenceUntrackStatus)
9
}
10
11
untrackPresence()

Presence options#

You can pass configuration options while initializing the Supabase Client.

Presence key#

By default, Presence will generate a unique UUIDv1 key on the server to track a client channel's state. If you prefer, you can provide a custom key when creating the channel. This key should be unique among clients.

1
import { createClient } from '@supabase/supabase-js'
2
const supabase = createClient('SUPABASE_URL', 'SUPABASE_PUBLISHABLE_KEY')
3
4
const channelC = supabase.channel('test', {
5
config: {
6
presence: {
7
key: 'userId-123',
8
},
9
},
10
})