Zoom Video SDK Web (v2.4.0)
Expert guidance for building video sessions with Zoom Video SDK for Web.
This skill is for custom video sessions, not embedded Zoom meetings.
If the user wants a custom UI for a real Zoom meeting, route to ../../meeting-sdk/web/component-view/SKILL.md.
Use ../../probe-sdk/SKILL.md as an optional browser/device/network readiness gate before client.join(...).
Quick Start
Installation
bun install @zoom/videosdk --save
# or
npm install @zoom/videosdk --save
Basic Session Setup
import ZoomVideo from "@zoom/videosdk";
// 1. Create client
const client = ZoomVideo.createClient();
// 2. Initialize SDK
await client.init("en-US", "Global", { patchJsMedia: true });
// 3. Join session (requires JWT token from your server)
await client.join(sessionName, jwtToken, userName, sessionPassword);
// 4. Get media stream for audio/video control
const stream = client.getMediaStream();
Core API Reference
ZoomVideo (Static Methods)
| Method | Description |
|---|---|
createClient() |
Create VideoClient instance (singleton) |
checkSystemRequirements() |
Check browser compatibility → { audio, video, screen } |
checkFeatureRequirements() |
Get supported/unsupported features list |
getDevices(skipPermissionCheck?) |
Enumerate media devices |
createLocalAudioTrack(deviceId?) |
Create local audio track for preview |
createLocalVideoTrack(deviceId?) |
Create local video track for preview |
destroyClient() |
Destroy client instance |
preloadDependentAssets(path?) |
Preload WebAssembly/Worker assets |
VideoClient Methods
Session Management
// Initialize before joining
await client.init(language, dependentAssets, options?);
// Join session
await client.join(topic, token, userName, password?, idleTimeoutMins?);
// Leave or end session
await client.leave(end?); // end=true ends for all (host only)
User Management
client.getCurrentUserInfo(): Participant;
client.getAllUser(): Participant[];
client.getUser(userId): Participant | undefined;
client.getSessionHost(): Participant | undefined;
// Host/Manager actions
client.makeHost(userId); // Transfer host
client.makeManager(userId); // Promote to manager
client.revokeManager(userId); // Revoke manager
client.removeUser(userId); // Remove participant
client.changeName(name, userId?);
Feature Clients
client.getMediaStream(); // Audio/Video/Screen share
client.getChatClient(); // In-session chat
client.getCommandClient(); // Custom signaling
client.getRecordingClient(); // Cloud recording
client.getSubsessionClient(); // Breakout rooms
client.getLiveTranscriptionClient(); // Captions
client.getLiveStreamClient(); // RTMP streaming
client.getWhiteboardClient(); // Whiteboard
MediaStream (Audio/Video Control)
Audio
const stream = client.getMediaStream();
// Start audio (requires user gesture)
await stream.startAudio({
mute?: boolean,
speakerOnly?: boolean,
backgroundNoiseSuppression?: boolean,
microphoneId?: string,
speakerId?: string,
});
// Control
await stream.muteAudio();
await stream.unmuteAudio();
stream.stopAudio();
// Device management
stream.getMicList(): MediaDevice[];
stream.getSpeakerList(): MediaDevice[];
await stream.switchMicrophone(deviceId);
await stream.switchSpeaker(deviceId);
Video
// Start video (simple call, no options needed)
await stream.startVideo();
// Or with options
await stream.startVideo({
cameraId?: string,
hd?: boolean, // 720p
fullHd?: boolean, // 1080p
mirrored?: boolean,
virtualBackground?: { imageUrl: string | 'blur' | undefined, cropped?: boolean },
});
// CRITICAL: Attach video to DOM
// 1. Container MUST be a <video-player-container> custom element
// 2. attachVideo returns an element - append it to the container
// 3. Do NOT pass container as third parameter
const videoElement = await stream.attachVideo(userId, VideoQuality.Video_720P);
container.appendChild(videoElement);
// Stop video
await stream.stopVideo();
// Detach video (cleanup)
stream.detachVideo(userId);
// Device management
stream.getCameraList(): MediaDevice[];
await stream.switchCamera(deviceId);
Screen Share
// Start sharing
await stream.startShareScreen({
broadcastToSubsession: boolean,
optimizedForSharedVideo: boolean,
secondaryCameraId: string, // Share secondary camera
});
// Stop sharing
await stream.stopShareScreen();
// View others' share
await stream.startShareView(canvas, userId);
stream.stopShareView();
Events
// Connection
client.on("connection-change", (payload) => {
// payload.state: 'Connected' | 'Reconnecting' | 'Closed' | 'Fail'
});
// Users
client.on("user-added", (participants: Participant[]) => {});
client.on("user-updated", (participants: Participant[]) => {});
client.on("user-removed", (participants: Participant[]) => {});
// Audio
client.on("current-audio-change", (payload) => {
// payload.action: 'join' | 'leave' | 'muted' | 'unmuted'
});
client.on("active-speaker", (payload) => {
// payload.activeSpeaker: { oderId: number, oderId?: number }[]
});
// Video
client.on("video-active-change", (payload) => {
// payload.userId, payload.state: 'Active' | 'Inactive'
});
client.on("peer-video-state-change", (payload) => {
// payload.userId, payload.action: 'Start' | 'Stop'
});
// Screen Share
client.on("active-share-change", (payload) => {
// payload.userId, payload.state: 'Active' | 'Inactive'
});
client.on("peer-share-state-change", (payload) => {
// payload.userId, payload.action: 'Start' | 'Stop'
});
// Chat
client.on("chat-on-message", (payload) => {
// payload.message, payload.sender, payload.timestamp
});
// Device
client.on("device-change", () => {
// Re-enumerate devices
});
Framework-Specific Implementation Guides
For complete implementation examples with full project setup, hooks/composables/services, and components:
- references/react.md - React 19 + Vite 7 + TypeScript + shadcn/ui implementation
- references/vue.md - Vue 3 + Vite 7 + TypeScript + shadcn-vue implementation
- references/angular.md - Angular 21 + Standalone Components + Signals implementation
- references/svelte.md - Svelte 5 + Runes + Vite 7 + TypeScript implementation
Quick Setup Requirements
All frameworks MUST configure:
- COOP/COEP Headers (for SharedArrayBuffer support):
// vite.config.ts
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
}
- TypeScript Custom Elements (for video rendering):
// src/types/zoom-elements.d.ts
declare namespace JSX {
interface IntrinsicElements {
"video-player-container": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
>;
}
}
Core Implementation Pattern
All implementations should follow this pattern:
- Client Management - Singleton client with init/join/leave lifecycle
- Media Stream - Audio/video/screen share controls with state sync
- Participants - User list with video state change listeners
- Video Rendering - Use
video-player-container+attachVideo() - Event Handling - Connection, audio, video, chat events
- Error Handling - Map SDK error codes to user messages
JWT Token Requirements
Generate JWT on your server using HMAC SHA256. Never expose SDK secret in client code.
Required Claims
{
app_key: 'YOUR_SDK_KEY', // Video SDK key
tpc: 'session-name', // Session name (max 200 chars), must match join() topic
role_type: 1, // 1 = host/co-host, 0 = participant
version: 1, // Always set to 1
iat: Math.floor(Date.now() / 1000) - 30, // Issued at (subtract 30s for clock skew)
exp: iat + 7200, // Expiration: min 1800s, max 48 hours after iat
}
Optional Claims
| Claim | Description |
|---|---|
user_key |
Unique user identifier (max 36 chars) |
session_key |
Session identifier for cloud recording (max 36 chars) |
geo_regions |
Data center regions: AU,BR,CA,DE,HK,IN,JP,CN,MX,NL,SG,US |
cloud_recording_option |
0 = combined video, 1 = separate files per user |
cloud_recording_election |
1 to record user's self-view |
cloud_recording_transcript_option |
0 = none, 1 = transcript, 2 = transcript + summary |
video_webrtc_mode |
0 = disable, 1 = enable WebRTC video |
audio_webrtc_mode |
1 = enable WebRTC audio (Web only) |
telemetry_tracking_id |
For Web SDK telemetry tracking |
Node.js(jsrsasign) Quick Start JWT Generator Service:
git clone https://github.com/zoom/videosdk-auth-endpoint-sample.git
cd videosdk-auth-endpoint-sample
bun install
# or
npm install
# Rename .env.example to .env, edit the file contents to include your Zoom Video SDK key and secret, save the file contents, and close the file
mv .env.example .env
# Start the server
# server code https://raw.githubusercontent.com/zoom/videosdk-auth-endpoint-sample/refs/heads/master/src/index.js
bun run start
# or
npm run start
jsrsasign Example Code:
import { KJUR } from "jsrsasign";
const iat = Math.floor(Date.now() / 1000) - 30;
const exp = iat + 60 * 60 * 2; // 2 hours
const payload = {
app_key: process.env.ZOOM_SDK_KEY,
tpc: "session-name",
role_type: 1,
version: 1,
video_webrtc_mode: 1,
audio_webrtc_mode: 1,
iat,
exp,
};
const token = KJUR.jws.JWS.sign(
"HS256",
JSON.stringify({ alg: "HS256", typ: "JWT" }),
JSON.stringify(payload),
process.env.ZOOM_SDK_SECRET
);
Init Options
interface InitOptions {
webEndpoint?: string; // Custom endpoint
enforceMultipleVideos?: boolean | { disableRenderLimits?: boolean };
enforceVirtualBackground?: boolean;
stayAwake?: boolean; // Prevent screen dimming
leaveOnPageUnload?: boolean; // Quick leave on page close
patchJsMedia?: boolean; // Apply latest media fixes (recommended: true)
alternativeNameForVideoPlayer?: string;
}
Video Quality Enum
enum VideoQuality {
Video_90P = 0,
Video_180P = 1,
Video_360P = 2,
Video_720P = 3,
Video_1080P = 4,
}
Best Practices
- Always check browser support before initializing
- Generate JWT on server - never expose SDK secret in client
- Handle connection events for robust session management
- Request user gesture before starting audio (browser requirement)
- Use
patchJsMedia: truefor latest WebAssembly fixes - Set
leaveOnPageUnload: truefor clean disconnection - Implement error handling for all async operations
- Clean up event listeners on session end
Common Issues
| Issue | Solution |
|---|---|
| Audio doesn't start | Requires user gesture (click/tap) |
| Video not rendering | Use video-player-container custom element and append attachVideo() result |
| JWT invalid | Verify tpc matches session name |
| SharedArrayBuffer error | MUST add COOP/COEP headers in vite.config.ts |
OPERATION_TIMEOUT on startVideo |
Check camera permissions and ensure COOP/COEP headers are set |
INVALID_PARAMETERS on attachVideo |
Don't pass container as 3rd param - append the returned element instead |
| Self-video not showing | Pass currentUserVideoOn prop to VideoGrid (participant.bVideoOn may lag) |
| Participants not updating | Listen for video-active-change and peer-video-state-change events |
| "Waiting for participants" stuck | Pass isJoined to useParticipants hook and listen for connection-change |
Detailed Guides
Core Concepts (read these first — they apply to every feature)
- concepts/sdk-architecture-pattern.md - The universal 5-step pattern (
createClient → init → join → getMediaStream → use) that every feature follows - concepts/singleton-hierarchy.md - Service-locator navigation tree from
ZoomVideoroot to every sub-client (media, chat, recording, subsession, command, etc.)
Feature References
- references/sessions.md - Session lifecycle, user roles, JWT token, connection events
- references/audio.md - Audio controls, devices, muting, dial-out, noise suppression
- references/video.md - Video capture, rendering, virtual background, PTZ cameras
- references/screen-share.md - Screen sharing, annotation, tab audio, share privileges
- references/features.md - Preview, recording, subsessions, live stream, command channel, transcription, PSTN, SIP, quality stats
- references/advanced.md - Chat, custom processors, whiteboard
- references/browser-support.md - Browser compatibility, SharedArrayBuffer, CSP headers
- references/error-codes.md - Complete error code reference
- troubleshooting/common-issues.md - Troubleshooting guide: symptoms → causes → fixes for the most common Video SDK bugs
Type Definitions Location
All TypeScript definitions are in @zoom/videosdk/dist/types:
index.d.ts- Main exportszoomvideo.d.ts- ZoomVideo namespacevideoclient.d.ts- VideoClient methods and eventsmedia.d.ts- MediaStream, audio/video optionscommon.d.ts- Shared types (Participant, enums)chat.d.ts- Chat clientrecording.d.ts- Recording clientsubsession.d.ts- Breakout rooms
Resources Homepage
- Official Docs: https://developers.zoom.us/docs/video-sdk/web/
- API Reference: https://marketplacefront.zoom.us/sdk/videosdk/web/
- Sample App: https://github.com/zoom/videosdk-web-sample
- Angular: https://developers.zoom.us/docs/video-sdk/web/frameworks/#using-angular
- Requirejs: https://developers.zoom.us/docs/video-sdk/web/frameworks/#using-amd-mode-requirejs-or-named-modules
Official Docs URLs
# Main Documentation
https://developers.zoom.us/docs/video-sdk/web/get-started/
https://developers.zoom.us/docs/video-sdk/web/sessions/
https://developers.zoom.us/docs/video-sdk/web/event-handling/
# Audio & Video
https://developers.zoom.us/docs/video-sdk/web/video/
https://developers.zoom.us/docs/video-sdk/web/audio/
https://developers.zoom.us/docs/video-sdk/web/preview/
# Screen Sharing
https://developers.zoom.us/docs/video-sdk/web/share/
https://developers.zoom.us/docs/video-sdk/web/share-annotation/
https://developers.zoom.us/docs/video-sdk/web/share-browser-options/
# Advanced Features
https://developers.zoom.us/docs/video-sdk/web/recording/
https://developers.zoom.us/docs/video-sdk/web/subsessions/
https://developers.zoom.us/docs/video-sdk/web/live-stream/
https://developers.zoom.us/docs/video-sdk/web/command-channel/
https://developers.zoom.us/docs/video-sdk/web/transcription-translation/
https://developers.zoom.us/docs/video-sdk/web/chat/
# Phone Integration
https://developers.zoom.us/docs/video-sdk/web/pstn/
https://developers.zoom.us/docs/video-sdk/web/sip/
# Quality & Compatibility
https://developers.zoom.us/docs/video-sdk/web/quality/
https://developers.zoom.us/docs/video-sdk/web/browser-support/
https://developers.zoom.us/docs/video-sdk/web/sharedarraybuffer/
https://developers.zoom.us/docs/video-sdk/web/error-codes/
# Other
https://developers.zoom.us/docs/video-sdk/web/virtual-background/
https://developers.zoom.us/docs/video-sdk/web/frameworks/
Repo-Local Appendices
The documents below provide deeper examples, compatibility shims, and operational debugging.
Operational Docs
- RUNBOOK.md - 5-minute preflight checks before deep debugging
Compatibility References
- references/web.md - compatibility index for repo links that previously pointed to the monolithic web reference
- references/web-reference.md - repo-local extended API reference
- references/events-reference.md - repo-local event catalog and payload notes
- troubleshooting/common-issues.md - canonical troubleshooting guide from the imported skill
- references/common-issues.md - compatibility pointer for upstream-style reference links
Complementary Examples
- examples/session-join-pattern.md - complete join flow example
- examples/video-rendering.md - repo-local rendering patterns
- examples/event-handling.md - detailed event handling examples
- examples/screen-share.md - screen sharing send/view patterns
- examples/chat.md - in-session messaging examples
- examples/command-channel.md - custom signaling examples
- examples/recording.md - recording control examples
- examples/transcription.md - live transcription examples
- examples/react-hooks.md -
@zoom/videosdk-reactpatterns - examples/framework-integrations.md - repo-local SSR and framework notes