Presence
Presence tells your application who is currently subscribed to a channel, in real time, without polling. You get presence data through the same SDK event listener you already use for messages. No separate connection or subscription is needed. Where Pub/Sub delivers what a client sent, Presence answers a different question: who is here right now and what changed. This page explains:
- how PubNub generates and delivers presence data
- the three things you can do with it: query occupancy, attach live state, and receive change events
- how presence differs from the persistent membership records your application also keeps
- how to turn presence on for some or all channels without writing code
One property runs through everything below: presence is live state, not stored state. A client's occupancy, state, and event history exist only while that client stays connected, and nothing in Presence keeps a record once it disconnects. If you need a durable answer to "who belongs to this channel", that's membership, a separate feature.
How presence delivers its data
When Presence is enabled on your keyset, PubNub creates a companion channel for every channel you subscribe to. It's named after the channel with a -pnpres suffix: chat.room1 gets chat.room1-pnpres. PubNub publishes a channel's presence events on that companion channel, over the same real-time delivery path as ordinary messages.
That shared delivery path is why presence needs no separate connection or polling loop. Subscribing with the receivePresenceEvents option (the exact name varies by SDK) subscribes you to the companion channel automatically. You then read presence events with the same event listener you already use for messages and signals, through a dedicated onPresence handler.
The following is illustrative rather than a complete program. It enables receivePresenceEvents on a subscription and logs each event that arrives:
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
1const channel = pubnub.channel('channel_1');
2const subscription = channel.subscription({ receivePresenceEvents: true });
3
4subscription.onPresence = (event) => console.log('Presence event:', event);
5
6subscription.subscribe();
1subscription = pubnub.channel('channel_1').subscription(with_presence=True)
2
3subscription.on_presence = lambda presence: print('Presence event:', presence.event, presence.uuid)
4
5subscription.subscribe()
1Channel channel = pubnub.channel("channel_1");
2Subscription subscription = channel.subscription(SubscriptionOptions.receivePresenceEvents());
3
4subscription.setOnPresence(result ->
5 System.out.println("Presence event: " + result.getEvent() + " " + result.getUuid()));
6
7subscription.subscribe();
1val subscription = pubnub.channel("channel_1").subscription(SubscriptionOptions.receivePresenceEvents())
2
3subscription.onPresence = { presence -> println("Presence event: ${presence.event} ${presence.uuid}") }
4
5subscription.subscribe()
1Subscription subscription = pubnub.Channel("channel_1").Subscription(SubscriptionOptions.ReceivePresenceEvents);
2
3subscription.onPresence += (Pubnub pn, PNPresenceEventResult e) =>
4 Console.WriteLine("Presence event: " + e.Event);
5
6subscription.Subscribe<object>();
1listener := pubnub.NewListener()
2
3go func() {
4 for presence := range listener.Presence {
5 fmt.Println("Presence event:", presence.Event, presence.UUID)
6 }
7}()
8
9pn.AddListener(listener)
10pn.Subscribe().Channels([]string{"channel_1"}).WithPresence(true).Execute()
1let subscription = pubnub
2 .channel("channel_1")
3 .subscription(Some(vec![SubscriptionOptions::ReceivePresenceEvents]));
4subscription.subscribe();
5
6tokio::spawn(subscription.presence_stream().for_each(|presence| async move {
7 println!("Presence event: {:?}", presence);
8}));
New SDK available
C-Core (legacy) is still supported. If you are starting a new project, use the new C SDK.
1if (PNR_STARTED == pubnub_subscribe(pubnub, "channel_1-pnpres", NULL)) {
2 pubnub_await(pubnub);
3}
4
5for (char const *event = pubnub_get(pubnub); NULL != event; event = pubnub_get(pubnub)) {
6 printf("Presence event: %s\n", event);
7}
1let subscription = pubnub.channel("channel_1").subscription(options: ReceivePresenceEvents())
2
3subscription.onPresence = { presenceChange in
4 print("Presence event: \(presenceChange)")
5}
6
7subscription.subscribe()
1- (void)client:(PubNub *)client didReceivePresenceEvent:(PNPresenceEventResult *)event {
2 NSLog(@"Presence event: %@", event.data.presenceEvent);
3}
4
5[self.pubnub subscribeToChannels:@[@"channel_1"] withPresence:YES];
1final subscription = pubnub.subscribe(channels: {'channel_1'}, withPresence: true);
2
3subscription.presence.listen((event) {
4 print('Presence event: ${event.action}');
5});
1class PresenceListener extends SubscribeCallback
2{
3 public function presence($pubnub, $presence)
4 {
5 echo 'Presence event: ' . $presence->getEvent() . PHP_EOL;
6 }
7}
8
9$pubnub->addListener(new PresenceListener());
10$pubnub->subscribe()->channels('channel_1')->withPresence()->execute();
1callback = Pubnub::SubscribeCallback.new(
2 presence: ->(envelope) { puts "Presence event: #{envelope.result}" }
3)
4
5pubnub.add_listener(callback: callback)
6pubnub.subscribe(channels: ['channel_1'], with_presence: true)
1listener.onPresence += (Pubnub pn, PNPresenceEventResult e) => Debug.Log("Presence event: " + e.Event);
2
3subscription = pubnub.Channel("channel_1").Subscription(SubscriptionOptions.ReceivePresenceEvents);
4subscription.Subscribe<object>();
1Subscription->OnPubnubPresenceEvent.AddDynamic(this, &AMyActor::OnPresenceEventReceived);
2Subscription->SubscribeAsync();
3
4void AMyActor::OnPresenceEventReceived(FPubnubMessageData Message)
5{
6 UE_LOG(LogTemp, Log, TEXT("Presence event: %s, Channel: %s"), *Message.Message, *Message.Channel);
7}
For the full subscription call and listener setup, refer to Receive presence events.
Presence identifies clients by User ID, the same identity Pub/Sub, access control, and billing use. One consequence is specific to presence. If several devices connect with the same User ID, they count as one user for billing. But each device's connection and disconnection still generates its own presence events. A person who closes one tab while staying connected on another can produce a leave immediately followed by a join, even though they never went offline.
Query who's online right now
Call Here Now to ask a channel, on demand, how many clients are subscribed, who they are, and what state each one has set. It's the fetch counterpart to presence events. Use it once to seed a screen, such as an online-friends list. Then rely on presence events to keep that list current instead of calling Here Now again.
A companion call, Where Now, answers the opposite question: which channels a specific User ID is currently subscribed to. It's rarely needed outside testing and troubleshooting, because most applications already know which channels a client subscribes to.
For occupancy limits, response caching, and count-only queries on high-traffic channels, refer to Occupancy. For the calls themselves, refer to Get online users in a channel and Get subscribed channels for a user.
Attach state to a subscription
Presence state is custom data, such as a mood, a game score, or a typing indicator. You attach it to a User ID on a channel while that User ID stays subscribed. Other subscribers can read it with Here Now or with a dedicated Get State call, and a state-change presence event fires whenever you set it.
State lives only as long as the subscription that carries it. PubNub doesn't persist presence state, and disconnecting a client clears it, so a client that reconnects has to set its state again if it still applies. That's what separates presence state from App Context, which stores structured metadata about users and channels indefinitely regardless of connection status. Use presence state for something that's only meaningful while a client is online, and App Context for anything that needs to survive a disconnect.
For the calls that set and read state, refer to Set and get presence state.
React to arrivals and departures in real time
Beyond state-change, presence generates events for the rest of a client's lifecycle on a channel. join fires when it subscribes, leave when it unsubscribes, and timeout when it stops responding for longer than PubNub's heartbeat allows. PubNub SDKs set presenceTimeout to 300 seconds by default, which is how long PubNub waits without a heartbeat before marking a client offline. You can shorten that window with explicit heartbeats when your application needs faster disconnect detection than the default gives you.
Below the Announce Max threshold, a channel emits individual join, leave, and timeout events. At or above it, the channel emits periodic interval events instead, while state-change stays individual in both modes. This keeps a busy channel's presence traffic from growing in step with its occupancy. For the event payloads, the announce and interval modes, and how to tune heartbeat timing, refer to Presence events. For the subscription call, refer to Receive presence events.
Presence and membership answer different questions
Presence and membership are easy to conflate, because both relate a User ID to a channel, but one is live and the other is stored. Presence tells you who is on a channel right now. Membership tells you who belongs to it, connected or not. Refer to Core concepts for the full comparison, including how to combine both into one roster that distinguishes online members from offline ones.
Turn on presence without writing code
Presence must be enabled on the keyset, and new keysets have it enabled by default. Beyond that switch, how much presence data you generate is a configuration choice, not a coding one. You make that choice in Presence Management, under BizOps Workspace in the Admin Portal, rather than in your application code.
There, you choose between two starting points. Tracking selected channels only, the recommended default, keeps presence events off until you add a rule naming which channel patterns and event types to track. Tracking all channels turns every presence event on for every channel, which is simpler to reason about but can increase cost on high-traffic keysets. A rule can also set the occupancy threshold that triggers interval mode, the interval cadence, and whether a dropped TCP connection reports a leave instead of a timeout. Because these are keyset-level settings, changing them takes effect for every connected client without a client-side release.
Presence and access control
When Access Manager is enabled on your keyset:
-
When Access Manager is enabled, calling
Here Nowrequiresreadpermission on the queried channel. -
Where Nowrequires no channel-level permission when Access Manager is enabled because it looks up a User ID rather than a channel. -
When Access Manager is enabled, receiving presence events requires
readpermission on the channel, which also grants access to its-pnprescompanion channel. -
When Access Manager is enabled,
Get StateandSet Stateboth requirereadpermission on the channel.
Next steps
- Presence events. Event types, payloads, announce and interval modes, and heartbeat configuration.
- Occupancy.
Here Nowin depth: limits, caching, and count-only queries. - Receive presence events. Subscribe to a channel with presence events enabled and register a listener.
- Get online users in a channel. Call
Here Nowto list current subscribers and their state. - Set and get presence state. Attach and read custom state on a subscription.
- Get subscribed channels for a user. Call
Where Nowfor a specific User ID. - Core concepts. Channels, User IDs, and membership.
- Presence Management. Configure presence rules in the Admin Portal.