Skip to main content

ReactJS Host Dashboard Tutorial

Build a host dashboard with mediasfu-reactjs@4.3.0. The supplied room renders participants, requests, permissions, media, alerts, and role-aware exit.

Prerequisites

  • Node.js 18 to 20, React 18, HTTPS or localhost, and two browser profiles.
  • Authenticated create/join routes shown below.
  • A backend policy that identifies hosts and allowed moderation actions.

Project files

package.json
{"private":true,"scripts":{"dev":"vite"},"dependencies":{"mediasfu-reactjs":"4.3.0","react":"18.2.0","react-dom":"18.2.0"},"devDependencies":{"@types/react":"18.3.24","@types/react-dom":"18.3.7","@vitejs/plugin-react":"4.3.4","typescript":"5.9.3","vite":"5.4.19"}}
src/App.tsx
import { useState } from 'react';
import { ModernMediasfuGeneric, type CreateRoomOnMediaSFUType, type JoinRoomOnMediaSFUType, type Participant } from 'mediasfu-reactjs';

async function postRoom<T>(url: string, payload: unknown): Promise<T> {
const response = await fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!response.ok) throw new Error(`Room request failed (HTTP ${response.status}).`);
return response.json() as Promise<T>;
}
const createRoom: CreateRoomOnMediaSFUType = ({ payload }) => postRoom('/api/mediasfu/create-room', payload);
const joinRoom: JoinRoomOnMediaSFUType = ({ payload }) => postRoom('/api/mediasfu/join-room', payload);
type RoomState = { participants?: Participant[] };

export default function App() {
const [room, setRoom] = useState<RoomState>({});
return <main><header><h1>Host dashboard</h1><p>Open Participants and Requests to manage the current room.</p></header>
<ModernMediasfuGeneric connectMediaSFU={true} createMediaSFURoom={createRoom} joinMediaSFURoom={joinRoom} sourceParameters={room} updateSourceParameters={setRoom} />
<output aria-live="polite">Current participants: {room.participants?.length ?? 0}</output>
</main>;
}
src/main.tsx
import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App';
createRoot(document.getElementById('root')!).render(<StrictMode><App /></StrictMode>);
index.html
<!doctype html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>Host dashboard</title></head><body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body></html>
vite.config.ts
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()] });
tsconfig.json
{"compilerOptions":{"target":"ES2022","lib":["DOM","ES2022"],"strict":true,"module":"ESNext","moduleResolution":"Bundler","skipLibCheck":true,"noEmit":true,"jsx":"react-jsx"},"include":["src"]}

For isolated local development, your backend may use restricted, revocable development credentials that are never committed. Before distribution, external testing, or production rooms, reusable Cloud credentials must remain only on the authenticated backend.

Backend

The complete backend request flow is in the public production room credential boundary. Have /api/mediasfu/create-room and /api/mediasfu/join-room authenticate the caller before they forward a room request. The browser never receives a MediaSFU Cloud credential.

Run

npm install
npm run dev

Expected result

Create a room as host and join from the second profile. Open Participants and Requests, apply an allowed action, and confirm the room state changes for both people. A participant cannot use host-only controls.

Recovery

Disable moderation during reconnect. Treat a denied action as policy, not a network retry. If no participant is selected or room state has changed, ask the host to choose again rather than applying an action to stale state.

Cleanup

Use participant Leave for a local exit and the supplied host End flow only for the room-wide action. Clear app-held selection state and expire temporary room authority on the backend.