Migration Guide - v0.x.x → v1.0.0
1. Promise-based API
All methods returned by useMeeting, useParticipant, useMediaStream, useTranscription, and other SDK hooks are asynchronous and return a Promise. Always await them.
- If a method fails, it throws an error that you should handle with
try/catch. - The
onErrorcallback onuseMeeting({ onError })continues to receive the same errors. Use it for centralized error handling, whiletry/catchlets you handle errors specific to an individual method call.
Before - fire-and-forget call; errors arrived only on the onError callback
const { enableMic } = useMeeting({
onError: (err) => console.error(err.code, err.message),
});
enableMic();
After - errors are thrown from the awaited call (and also emitted on onError)
const { enableMic } = useMeeting({
// onError still fires for the same errors
onError: (err) => console.error(err.code, err.message),
});
const handleEnableMic = async () => {
try {
await enableMic();
console.log("mic on");
} catch (err) {
console.error(err.name, err.code, err.message);
}
};
2. PubSub (usePubSub)
The hook still auto-subscribes on mount and auto-unsubscribes on unmount. What changed: new signature usePubSub(topic, listeners?, properties?), extra callbacks and properties.
2.1 New callbacks and properties on usePubSub
Before
import { usePubSub } from "@videosdk.live/react-sdk";
function ChatView() {
const { publish, messages } = usePubSub("CHAT", {
onMessageReceived: (msg) => console.log("live:", msg),
// In v0.x.x, onOldMessagesReceived took only the messages array - no second arg
onOldMessagesReceived: (msgs) => console.log("history:", msgs),
// Existing properties in v0.x.x
maxMessages: 100,
bufferMessages: true,
});
return <>...</>;
}
After
import { usePubSub, Constants } from "@videosdk.live/react-sdk";
function ChatView() {
const { publish, messages } = usePubSub(
"CHAT",
// 2nd arg: listeners
{
onMessageReceived: (msg) => console.log("live:", msg),
// New: array of realtime messages, grouped for bulk handling
onBatchReceived: (batch) => console.log("batch:", batch.length),
// New: called with persisted history; { isLast } is true on the final batch
onOldMessagesReceived: (msgs, { isLast }) =>
console.log("history:", msgs, isLast),
// New: fires when messages are dropped due to slow network / CPU
onMessageDrop: ({ droppedCount }) =>
console.warn("dropped:", droppedCount),
},
// 3rd arg: properties
{
// Existing (React-specific)
maxMessages: 100, // cap on the messages array (null = unlimited). Default: null
bufferMessages: true, // when false, only callbacks fire - messages array stays empty. Default: true
// New (align with JS SDK subscribe options)
oldMessageLimit: 50, // how many stored messages to fetch. Default: all
newMessageLimit: 20, // max realtime messages received per 500 ms
realtimeOverflow: Constants.RealtimeOverflow.QUEUE, // "queue" | "drop". Default: "queue"
maxQueue: 100, // only valid when realtimeOverflow === "queue". Default: 70, max: 200
},
);
return <>...</>;
}
Key points:
- The hook signature is now three positional arguments:
(topic, listeners, properties). Previously listeners and properties were merged into a single second argument. - The second argument is a listeners object. New listeners:
onBatchReceived,onMessageDrop.onOldMessagesReceivednow receives{ isLast }as a second argument. - The third argument is a properties object (batching / history / backpressure). New properties:
oldMessageLimit,newMessageLimit,realtimeOverflow,maxQueue.maxMessagesandbufferMessagesare unchanged. - Learn more: Chat using PubSub
3. Track configuration is preserved
When you pass a media track to the SDK, its configuration is stored and reused automatically.
In v0.x.x, disabling and re-enabling a track—or switching modes with changeMode()—reset the track configuration to the SDK default. Starting with v1.0.0, the SDK preserves the most recently configured track and reuses it whenever the track is enabled again, including after transitioning back to a producing mode with changeMode().
The stored track can come from:
- The initial configuration provided to
MeetingProvider. - A later call to
enableMic()/enableWebcam()with a custom track. - A later call to
changeMic()/changeWebcam()with a custom track (see below).
changeMic() / changeWebcam() accept either a deviceId or a customTrack:
changeMic(deviceId)/changeWebcam(deviceId)— only the input device is swapped. All other settings from the stored config (encoderConfig,bitrateMode,multiStream,maxLayer,noiseConfig, etc.) are preserved.changeMic(customTrack)/changeWebcam(customTrack)— the entire track replaces the stored one. The new track becomes the "last config" and is reused on every subsequent enable and mode change.
Before
import {
MeetingProvider,
useMeeting,
createCameraVideoTrack,
Constants,
} from "@videosdk.live/react-sdk";
const camTrack = await createCameraVideoTrack({ encoderConfig: "h540p_w960p" });
<MeetingProvider
config={{
meetingId,
customCameraVideoTrack: camTrack,
webcamEnabled: true,
}}
>
<ChatView />
</MeetingProvider>;
function ChatView() {
const { disableWebcam, enableWebcam, changeMode } = useMeeting();
const run = async () => {
await disableWebcam();
await enableWebcam(); // ❌ default track (h720p_w1280p) - h540p_w960p dropped
await changeMode(Constants.modes.RECV_ONLY);
await changeMode(Constants.modes.SEND_AND_RECV); // ❌ default track (h720p_w1280p) - h540p_w960p dropped
};
}
After
import {
MeetingProvider,
useMeeting,
createCameraVideoTrack,
Constants,
} from "@videosdk.live/react-sdk";
const camTrack = await createCameraVideoTrack({ encoderConfig: "h540p_w960p" });
<MeetingProvider
config={{
meetingId,
customCameraVideoTrack: camTrack,
webcamEnabled: true,
}}
>
<ChatView />
</MeetingProvider>;
function ChatView() {
const { disableWebcam, enableWebcam, changeMode } = useMeeting();
const run = async () => {
await disableWebcam();
await enableWebcam(); // ✅ reuses h540p_w960p
// Provide a different track later - its config becomes the new "last config"
const camTrack2 = await createCameraVideoTrack({
encoderConfig: "h480p_w640p",
});
await enableWebcam(camTrack2);
await changeMode(Constants.modes.RECV_ONLY);
await changeMode(Constants.modes.SEND_AND_RECV); // ✅ reuses h480p_w640p
};
}
4. Behavior changes
4.1 Concurrent media and mode operations
enableMic(), disableMic(), enableWebcam(), disableWebcam(), and changeMode() now reject overlapping operations.
In v0.x.x, rapid user interactions or concurrent calls could race with one another, leaving the meeting or media state inconsistent. Starting with v1.0.0, the first operation proceeds normally, while any overlapping call throws the corresponding ERROR_OPERATION_IN_PROGRESS.
What you should do
- Prevent overlapping operations in your application. For example, disable your mic, webcam, and mode controls while an operation is in progress, and don't start another operation until the previous one completes.
4.2 Track creation methods now throw on failure
createCameraVideoTrack() and createMicrophoneAudioTrack() now throw on failure instead of returning null.
This affects failures such as:
- Permission denied
- No input device
- Invalid configuration
- Unsupported codec
Before
const track = await createCameraVideoTrack();
console.log(track); // null
After
try {
const track = await createCameraVideoTrack();
} catch (err) {
// Handle permission, missing device, invalid config, etc.
showError(err.message);
}
4.3 Media controls are only available in SEND_AND_RECV mode
enableMic(), disableMic(), enableWebcam(), and disableWebcam() can only be called when the meeting is in SEND_AND_RECV mode, which supports publishing media.
Starting with v1.0.0, these methods throw an error if the current meeting mode is not SEND_AND_RECV.
What you should do
Switch the meeting to SEND_AND_RECV before calling these methods:
import { Constants, useMeeting } from "@videosdk.live/react-sdk";
const { changeMode } = useMeeting();
await changeMode(Constants.modes.SEND_AND_RECV);
4.4 RealtimeStore.getValue() throws when a key doesn't exist
Previously, calling getValue() on an unset key returned null.
Starting with v1.0.0, getValue() throws FAILED_TO_GET_VALUE for unset keys. Update your code to treat this exception as the equivalent of a missing value.
4.5 All methods are now guarded before the meeting is joined
In v0.x.x, calling methods from useMeeting, useParticipant, useMediaStream, or usePubSub before the meeting was joined was inconsistent — some methods return with an error on the onError callback, while others silently no-op'd or produced undefined behavior.
Starting with v1.0.0, every method returned by these hooks throws ERROR_ACTION_PERFORMED_BEFORE_MEETING_JOINED if called before the onMeetingJoined callback has fired. Wait for onMeetingJoined before invoking any method.
Got a Question? Ask us on discord

