Skip to main content
Version: 1.x.x

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 onError callback on useMeeting({ onError }) continues to receive the same errors. Use it for centralized error handling, while try / catch lets 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. onOldMessagesReceived now receives { isLast } as a second argument.
  • The third argument is a properties object (batching / history / backpressure). New properties: oldMessageLimit, newMessageLimit, realtimeOverflow, maxQueue. maxMessages and bufferMessages are 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