Skip to main content

Build a Complete React Room

This guide builds the core room lifecycle with mediasfu-reactjs 4.3.0: secure create, join, participants, microphone and camera, remote media, screen sharing, participant leave, host leave-without-ending, and host end.

Before you start

  • Use React 18 or 19 and mediasfu-reactjs@4.3.0.
  • Add authenticated backend routes for room creation and joining.
  • Test microphone, camera, and display capture in a secure browser context.
  • Decide who can create a room, join it, and end it for everyone.

1. Keep credentials behind your backend

The room component accepts create and join adapters. Each adapter should send the requested room payload to your application backend; the backend attaches MediaSFU credentials after authenticating the user and checking room policy.

import type {
CreateRoomOnMediaSFUType,
JoinRoomOnMediaSFUType,
} from 'mediasfu-reactjs';

export const createRoomViaBackend: CreateRoomOnMediaSFUType = async ({ payload }) => {
const response = await fetch('/api/mediasfu/create-room', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});

if (!response.ok) throw new Error(`Create room failed: ${response.status}`);
return response.json();
};

export const joinRoomViaBackend: JoinRoomOnMediaSFUType = async ({ payload }) => {
const response = await fetch('/api/mediasfu/join-room', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});

if (!response.ok) throw new Error(`Join room failed: ${response.status}`);
return response.json();
};

The backend should authenticate the application user, enforce role and room policy, rate-limit requests, and return only the minimum room result. Do not log authorization headers, reusable room authority, or complete upstream responses.

2. Mount the complete room

import { useState } from 'react';
import {
ModernMediasfuGeneric,
type Participant,
} from 'mediasfu-reactjs';
import { createRoomViaBackend, joinRoomViaBackend } from './secure-room-adapters';

type RuntimeSnapshot = {
participants?: Participant[];
audioAlreadyOn?: boolean;
videoAlreadyOn?: boolean;
screenAlreadyOn?: boolean;
};

export function Room() {
const [runtime, setRuntime] = useState<RuntimeSnapshot>({});

return (
<main>
<ModernMediasfuGeneric
connectMediaSFU={true}
createMediaSFURoom={createRoomViaBackend}
joinMediaSFURoom={joinRoomViaBackend}
sourceParameters={runtime}
updateSourceParameters={setRuntime}
/>

<aside aria-live="polite">
<p>Participants: {runtime.participants?.length ?? 0}</p>
<p>Microphone: {runtime.audioAlreadyOn ? 'on' : 'off'}</p>
<p>Camera: {runtime.videoAlreadyOn ? 'on' : 'off'}</p>
<p>Screen: {runtime.screenAlreadyOn ? 'sharing' : 'not sharing'}</p>
</aside>
</main>
);
}

Keep the supplied UI for the first working version. It connects the room lifecycle, participant rendering, media controls, screen sharing, and role-aware exit flow. Move to a custom or headless shell only after this baseline works on the browsers and devices you support.

Know what success looks like

OperationWho can do itObservable successRecovery and cleanup
Secure createAn authenticated, authorized hostThe backend accepts the request and the room connects.Treat 401/403 as policy failures and 429 as backoff. Never fall back to a browser-held API key.
JoinAn authorized participantThe participant connects and appears in current room state.Explain invalid or expired access without exposing upstream details; allow a deliberate retry.
View participantsA connected room memberThe list and count change as people join and leave.Treat an empty list as valid; clear app-held snapshots on exit.
Prepare microphone and cameraA room member plus browser permissionThe selected device is ready and the produced state changes only after publishing begins.Distinguish denial, missing device, device loss, autoplay lock, and transport failure. Stop owned tracks on exit.
Publish microphone or cameraA permitted room memberLocal media is produced and another participant can receive it.Show the cause before retrying. Stop or disconnect the producer when toggled off.
Receive participant mediaA connected room memberRemote audio/video is connected and rendered according to browser policy.Distinguish no producer, receive-transport failure, paused media, and autoplay lock. Detach media on exit.
Share the screenA role allowed by room and browser policyCapture starts, remote participants can see it, and stopping removes the remote share and browser indicator.Treat chooser cancellation as normal. Stop the display track and screen producer together.
LeaveA participantThe confirmed leave action closes the local room and other participants see the departure.Cancel keeps the user in the room. Remove app listeners and stop local tracks after a confirmed leave.
End for everyoneAn authorized hostEvery participant observes the meeting end.Do not simulate host end by closing the tab or disconnecting only the local socket. Confirm server-side room cleanup.

Use the semantic leave and end flow

Use the supplied End action. It opens the role-aware confirmation UI: participants confirm leaving their session, while a host-level user confirms ending the meeting for everyone.

For a custom shell, use the headless room action and label the host choices separately:

import { useMediasfuHeadless } from 'mediasfu-reactjs';

export function HostExitButtons() {
const room = useMediasfuHeadless();
return <>
<button onClick={() => void room.controls.leave(false, true)}>End room</button>
<button onClick={() => void room.controls.leave(false, false)}>Leave and keep room open</button>
</>;
}

endRoomOnHostExit defaults to true; do not silently change that historical behavior. Pass false only for an explicit Leave and keep room open action. Await the result before unmounting so the host can later rejoin through the normal authorized join flow. See leave, end, and rejoin.

Test the complete journey

Run this acceptance flow with a host and a participant in separate browser contexts:

  1. Confirm an unauthenticated create or join request is rejected by your backend.
  2. Create a room as the host and join it as the participant.
  3. Open the participant list and confirm both users appear.
  4. Test microphone and camera permission approval, denial, and retry.
  5. Confirm each local track is received by the other participant.
  6. Start screen sharing, confirm the remote render, stop it, and confirm the browser sharing indicator clears.
  7. Leave as the participant and confirm the host sees the departure.
  8. End as the host and confirm every remaining client observes the meeting end.
  9. Verify temporary room authority and server-side room residue are removed according to your backend policy.

Build and test your application with its normal release commands. Those checks do not replace the browser, device, and two-user acceptance test above.

Continue from the working room