PubSub - React Native
PubSub is a concise acronym for the Publish-Subscribe mechanism. This mechanism is employed to send and receive messages within a specified topic. As the name implies, to send a message, one must specify the topic and the message to be published. Similarly, to receive a message, a subscriber must be connected to that particular topic.
Here is a visual to better understand the publish-subscribe mechanism.

This page uses the following asynchronous methods. Refer to their API reference for the errors each Promise may reject with, and handle these rejections appropriately based on your use case.
usePubSub
To utilize PubSub in a room, VideoSDK provides a hook called usePubSub. This hook enables you to subscribe to any topic and publish to any topic, so participants can exchange messages and instructions during the room.
The hook subscribes to the topic once the room is joined and unsubscribes when the component unmounts — you never call subscribe / unsubscribe yourself.
publish()
- This method is used for publishing a message for a specific topic.
- It can be accessed from the
usePubSubhook by specifying thetopicfor whichpublish()will be used. - It will accept following parameters as input:
message: This parameter represents the actual message to be published and should be inStringformat.options: This object specifies the options for publishing. You can set following properties :persist: When set to true, this option retains the message for the duration of the session. If persist is true, the message will be available for upcoming participants and can be accessed in the VideoSDK Session Dashboard in CSV format after the session is completed.sendOnly: If you want to send a message to specific participants, you can pass their respectiveparticipantIdhere. If you don't provide any IDs, the message will be sent to all participants by default.
payload: If you need to include additional information along with a message, you can pass it here as anobject.
// importing usePubSub hook from react-native-sdk
import { usePubSub } from "@videosdk.live/react-native-sdk";
import { TouchableOpacity, Text } from "react-native";
function RoomView() {
// destructure publish method from usePubSub hook
const { publish } = usePubSub("CHAT");
const handlePublishMessage = async () => {
// publish message
const message = "Hello Everyone!";
try {
await publish(message, { persist: true });
} catch (e) {
console.log("Error while sending message through pubsub", e);
}
};
return (
<>
<TouchableOpacity
onPress={() => {
handlePublishMessage();
}}
>
<Text>Publish Message</Text>
</TouchableOpacity>
</>
);
}
Sending structured data with payload
message must be a string. To send structured data, keep a short string in message and put the object in the third argument, payload. It arrives on the receiving side as message.payload, with no JSON.stringify or JSON.parse needed.
import { usePubSub } from "@videosdk.live/react-native-sdk";
import { TouchableOpacity, Text } from "react-native";
function RoomView() {
const { publish } = usePubSub("CHAT", {
onMessageReceived: (message) => {
// message.message is the string; message.payload is the object, unchanged.
/** @type {{ priority?: string, tags?: string[] }} */
const { priority, tags } = message.payload || {};
console.log(message.message, priority, tags);
},
});
const handlePublishMessage = async () => {
try {
// message: a string. options: an object. payload: your data, as an object.
await publish("Hello Everyone!", { persist: true }, { priority: "high", tags: ["announcement"] });
} catch (e) {
console.log("Error while sending message through pubsub", e);
}
};
return (
<TouchableOpacity onPress={handlePublishMessage}>
<Text>Publish Message</Text>
</TouchableOpacity>
);
}
usePubSub() callbacks and options
usePubSub(topic, listeners?, properties?)returns{ publish, messages }. The second argument is a listeners object (onMessageReceived,onBatchReceived,onOldMessagesReceived,onMessageDrop). The third argument is a properties object (maxMessages,bufferMessages,oldMessageLimit,realtimeOverflow,maxQueue,newMessageLimit).- It will accept following parameters as input:
topic: The topic to subscribe to and publish on.listeners: An object containing listener callbacks:-
onMessageReceived(message)— Called for every realtime message as soon as it is received.- Use this when: You want to handle each message individually — trigger a notification, play a sound, or update message-specific state.
- Don't use this when: The topic is high-throughput (busy chats, live reactions, cursor positions). Firing once per message causes excessive re-renders — use
onBatchReceivedinstead, or pair withbufferMessages: falseand throttle inside the callback.
-
onBatchReceived(messages)— Called with an array of realtime messages; the same set surfaced one-by-one throughonMessageReceived, grouped for bulk handling.- Use this when: A single state update per batch is cheaper than one per message (large chat lists, virtualized rendering).
-
onOldMessagesReceived(messages, { isLast })— Called with persisted history in batches — fires once per batch until history is fully delivered. Only messages published withpersist: trueare delivered here.isLastistrueon the final batch.- Use this when: Loading chat history or restoring previously sent messages when a participant joins the room.
-
onMessageDrop(info)— Called when incoming realtime messages cannot be delivered. This can happen:- because the client cannot keep up with the incoming message rate (for example, due to a slow network or high CPU usage), causing overflow, or
- because
newMessageLimitis configured, which intentionally limits how many realtime messages are delivered every 500 ms. Any messages exceeding the configured limit within that window are discarded.
info.droppedCountcontains the number of messages that were not delivered. It does not fire whenrealtimeOverflowis"drop".- Use this when: Notifying users that some realtime messages were missed, monitoring the effects of
newMessageLimit, or logging dropped messages for debugging and observability.
-
properties: This object specifies the hook properties. You can set following properties:maxMessages(number | null) — Hard cap on the number of messages retained in the returnedmessagesarray. Once the cap is reached, the oldest messages are dropped to make room for new ones. Passnullto retain every message. Default:null.- When to use: Cap history on high-frequency topics to prevent unbounded memory growth. For example, keep only the last 100 messages in a busy chat.
bufferMessages(boolean) — Controls whether incoming messages accumulate in the returnedmessagesarray. Whentrue(default), messages are stored inmessagesand delivered to the callbacks. Whenfalse, messages are delivered only via the callbacks and themessagesarray is never populated. Default:true.- When to use: Set to
falseon high-throughput topics (thousands of messages per second) where retaining a re-rendering array is undesirable — handle messages manually insideonMessageReceived/onBatchReceivedinstead.
- When to use: Set to
oldMessageLimit(number) — How many old messages to receive. Pass0to receive none. If omitted, all old messages are delivered. Must be0or more. Default: all.- When to use: Cap history for large sessions where loading every persisted message would be wasteful (e.g. only the last 50 messages need to appear when a participant joins).
realtimeOverflow("queue" | "drop") — Behavior when the client can't keep up."queue"(default) queues remaining messages so you receive them once you catch up;"drop"drops them instead.- When to use: Use
"queue"for chat where every message matters. Use"drop"for high-frequency, low-value streams (live reactions, cursor positions) where stale data is worse than missing data.
- When to use: Use
maxQueue(number) — Maximum number of message batches to queue during overflow. Default:70. Max:200. Must be1or more. Only valid withrealtimeOverflow: "queue".- When to use: Increase this value when you want to retain more messages and catch up after recovering from a slow network or CPU spike. Lower it if memory pressure matters more than history.
newMessageLimit(number) — Maximum number of realtime messages to receive per 500 ms. Leave it out, or pass0, for no limit.- When to use: Set this when you want to throttle a busy topic so the UI doesn't jank, or rate-limit a reactions topic.
Call usePubSub() once per topic and pass messages and publish to the components that need them. When a topic is already subscribed, another usePubSub() for it shares that subscription, so its oldMessageLimit, realtimeOverflow, maxQueue and newMessageLimit are not applied.
Every message in the messages array and every message delivered to the callbacks contains the following fields:
id— unique identifier for the message.message— the actual message content that was sent.senderId—participantIdof the participant who sent the message.senderName—displayNameof the participant who sent the message.timestamp— when the message was published, as a number of milliseconds since the epoch.topic— the topic the message was published to.payload— any additional data sent along with the message (optional).
// importing usePubSub hook from react-native-sdk
import { usePubSub } from "@videosdk.live/react-native-sdk";
import { TouchableOpacity, Text } from "react-native";
function ChatView() {
const listeners = {
onMessageReceived: (message) => {
console.log("New Message Received", message);
},
onBatchReceived: (messages) => {
// Handle a batch of realtime messages together
console.log(`Batch received with ${messages.length} messages`);
},
onOldMessagesReceived: (messages, { isLast }) => {
console.log("Old Messages published with persist:true Received", messages, isLast);
},
onMessageDrop: (info) => {
// Called when incoming messages are dropped
console.log(`Dropped ${info.droppedCount} messages`);
},
};
// destructure publish method and messages from usePubSub hook
const { publish, messages } = usePubSub("CHAT", listeners, {
maxMessages: 100,
bufferMessages: true,
oldMessageLimit: 50,
realtimeOverflow: "queue",
maxQueue: 70,
newMessageLimit: 20,
});
const handlePublishMessage = async () => {
// publish message
const message = "Hello Everyone!";
try {
await publish(message, { persist: true });
} catch (e) {
console.log("Error while sending message through pubsub", e);
}
};
return (
<>
<TouchableOpacity
onPress={() => {
handlePublishMessage();
}}
>
<Text>Publish Message</Text>
</TouchableOpacity>
<Text>Messages: </Text>
{messages.map((message) => {
return (
<Text key={message.id} style={{ fontSize: 12 }}>
{message.senderName} says {message.message}
</Text>
);
})}
</>
);
}
Errors
publish() throws:
ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED— called before the room is joined.ERROR_ROOM_RECONNECTING— the room is reconnecting.PUBSUB_PUBLISH_FAILED— the message could not be sent, for example because the connection to the room is down.
On Android, publish() resolves once the message is queued for sending. A failure after that is reported on onError of the useRoom hook instead.
The hook:
usePubSub() does not throw. Keep the properties within the ranges listed above, and do not combine maxQueue with realtimeOverflow: "drop". Properties that are refused are reported on onError of the useRoom hook as ERROR_INVALID_PARAMETER (3073). If the subscription fails because the connection to the room is down, PUBSUB_SUBSCRIBE_FAILED is reported on onError of the useRoom hook.
Applications of usePubSub
Some common uses of PubSub during a room:
Chat: You can utilise this to develop features, like Private Chat or Group Chat. You can follow our chat integration guide here.Raise Hand: You can allow attendees to raise their hands at any point during the room, informing everyone else that someone has a question or input.Layout Switching: You can change the room's layout for every participant at once during the room, such as from Grid layout to Spotlight or from Grid Layout to Sidebar,etc.Poll: You can make polls, let users respond to them, and display the results at the end of a poll.Question Answer Session: You can also design interactive features based on a question-and-answer format.
Downloading PubSub Messages
All the messages from PubSub published with persist : true can be downloaded as a .csv file. This file will be accessible in the VideoSDK dashboard and through the Sessions API.
API Reference
The API references for all the methods and events utilized in this guide are provided below.
Got a Question? Ask us on discord

