Video SDK 2.2.5: Self rendering video is not working, can anyone tell me what I am doing wrong

I am having trouble rendering the user (self) video for my meeting page for my web app using Video sdk 2.2.5. Can anyone check this code to see what I am doing wrong?

src/feature/zoom/video/VideoContainer.tsx

‘use client’;

import {
useContext,
useEffect,
useRef,
useState,
useMemo,
} from ‘react’;
import ZoomContext from ‘@/contexts/zoom-context’;
import ZoomMediaContext from ‘@/contexts/media-context’;
import { usePagination } from ‘@/feature/zoom/hooks/usePagination’;
import { useActiveVideo } from ‘@/feature/zoom/hooks/useActiveVideo’;
import { useAvatarAction } from ‘@/feature/zoom/hooks/useAvatarAction’;
import { useNetworkQuality } from ‘@/feature/zoom/hooks/useNetworkQuality’;
import { useCleanUp } from ‘@/feature/zoom/hooks/useCleanUp’;
import VideoFooter from ‘@/feature/zoom/video/video-footer’;
import Pagination from ‘@/feature/zoom/components/pagination’;
import RemoteCameraControlPanel from ‘@/feature/zoom/components/remote-camera-control’;
import AvatarActionContext from ‘@/contexts/avatar-context’;

import styles from ‘./VideoContainer.module.css’; // :white_check_mark: Use CSS module
import clsx from ‘clsx’;

interface Participant {
userId: number;
displayName?: string;
bVideoOn?: boolean;
}

interface ZoomClientMinimal {
getAllUser: () => Participant;
on: (event: string, callback: (…args: any) => void) => void;
off: (event: string, callback: (…args: any) => void) => void;
}

interface ZoomClientExtended {
getSessionInfo?: () => { isInMeeting: boolean };
getCurrentUserInfo?: () => { userId: number };
}

interface ZoomClientFull extends ZoomClientMinimal, ZoomClientExtended {}

type NetworkQuality = Record<number | string, { uplink: number; downlink: number }>;

export default function VideoContainer(): JSX.Element | null {
const zmClient = useContext(ZoomContext) as unknown as ZoomClientFull;
const {
mediaStream,
video: { decode: isVideoDecodeReady },
} = useContext(ZoomMediaContext);

const videoRef = useRef(null);
const [isRecieveSharing, setIsRecieveSharing] = useState(false);

const [visibleParticipants, setVisibleParticipants] = useState<Participant>();

useEffect(() => {
if (!zmClient) return;

const updateUsers = () => {
const updated = zmClient.getAllUser?.() ?? ;
setVisibleParticipants(updated);
};

zmClient.on(‘user-added’, updateUsers);
zmClient.on(‘user-removed’, updateUsers);

// Initial trigger
updateUsers();

return () => {
zmClient.off(‘user-added’, updateUsers);
zmClient.off(‘user-removed’, updateUsers);
};
}, [zmClient]);

const activeVideo = useActiveVideo(zmClient);
const rawNetworkQuality = useNetworkQuality(zmClient);
const networkQuality: NetworkQuality = useMemo(() => rawNetworkQuality ?? {}, [rawNetworkQuality]);
const currentUserId = zmClient.getCurrentUserInfo?.()?.userId ?? -1;
const avatarActionState = useAvatarAction(zmClient, visibleParticipants);
const isInMeeting = Boolean(zmClient.getSessionInfo?.()?.isInMeeting);
const { page, totalPage, setPage } = usePagination(zmClient, { width: 1, height: 1 });

useCleanUp(null, zmClient, mediaStream);

// :white_check_mark: Determine column layout class
const getGridColumnClass = (count: number): string => {
if (count <= 1) return styles[‘cols-1’];
if (count === 2) return styles[‘cols-2’];
if (count <= 6) return styles[‘cols-3’];
return styles[‘cols-3’];
};

// :white_check_mark: Always run this useEffect
useEffect(() => {
if (!mediaStream || !isVideoDecodeReady) return;

const renderStreams = () => {
visibleParticipants.forEach((user: Participant) => {
const canvasEl = document.getElementById(zoom-video-${user.userId}) as HTMLCanvasElement | null;
if (!canvasEl) return;

  // Always set dimensions fresh
  canvasEl.width = canvasEl.clientWidth;
  canvasEl.height = canvasEl.clientHeight;

  try {
    if (user.userId === currentUserId) {
      // ✅ Self view
      mediaStream.attachVideo(user.userId, canvasEl);
    } else if (user.bVideoOn) {
      // ✅ Remote participants
      mediaStream.renderVideo(canvasEl, user.userId, canvasEl.width, canvasEl.height, 0, 0, 3);
    }
  } catch (err) {
    console.error(`❌ Failed to render video for user ${user.userId}:`, err);
  }
});

};

renderStreams();

return () => {
visibleParticipants.forEach((user: Participant) => {
const canvasEl = document.getElementById(zoom-video-${user.userId}) as HTMLCanvasElement | null;
if (!canvasEl) return;

  try {
    if (user.userId === currentUserId) {
      mediaStream.detachVideo(user.userId);
    } else {
      mediaStream.stopRenderVideo(canvasEl, user.userId).catch((err: unknown) => {
        console.error(`❌ Failed to stop render for user ${user.userId}:`, err);
      });
    }
  } catch (err) {
    console.warn(`⚠️ Cleanup error for user ${user.userId}:`, err);
  }
});

};
}, [mediaStream, isVideoDecodeReady, visibleParticipants, currentUserId]);

useEffect(() => {
const update = () => zmClient.getAllUser?.();
zmClient.on(‘user-added’, update);
zmClient.on(‘user-removed’, update);
return () => {
zmClient.off(‘user-added’, update);
zmClient.off(‘user-removed’, update);
};
}, [zmClient]);

if (!mediaStream || !isVideoDecodeReady) return null;

return (


{/* Video Grid */}

<AvatarActionContext.Provider value={avatarActionState}>
<div
ref={videoRef}
className={clsx(
styles.gridWrapper,
getGridColumnClass(visibleParticipants.length)
)}
>
{visibleParticipants.map((user: Participant) => {
const isSelf = user.userId === currentUserId;

return (


<canvas
id={zoom-video-${user.userId}}
className=“w-full h-full object-cover”
/>

  {/* Name label */}
  <div className="absolute bottom-2 left-2 text-white text-xs bg-black/60 px-2 py-1 rounded">
    {isSelf ? 'You' : user.displayName || user.userId}
  </div>
</div>

);
})}

      </div>
    </AvatarActionContext.Provider>
  </div>

  {/* Footer */}
  <VideoFooter className="video-operations" sharing />

  {/* Pagination */}
  {totalPage > 1 && (
    <Pagination
      page={page}
      totalPage={totalPage}
      setPage={setPage}
      inSharing={isRecieveSharing}
    />
  )}
</div>

);
}

Hi @Erik1

Thanks for your feedback.

Could you share some problematic session IDs with us for troubleshooting purposes?

Thanks
Vic