mediasfu-reactjs
Version:
MediaSFU Prebuilt ReactJS SDK - Compatible with React 18 & 19, TypeScript & JavaScript
1,174 lines (938 loc) • 32.2 kB
Markdown
<p align="center">
<img src="https://www.mediasfu.com/logo192.png" width="100" alt="MediaSFU Logo">
</p>
<p align="center">
<a href="https://twitter.com/media_sfu">
<img src="https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white" alt="Twitter" />
</a>
<a href="https://www.mediasfu.com/forums">
<img src="https://img.shields.io/badge/Community-Forum-blue?style=for-the-badge&logo=discourse&logoColor=white" alt="Community Forum" />
</a>
<a href="https://github.com/MediaSFU">
<img src="https://img.shields.io/badge/GitHub-181717?style=for-the-badge&logo=github&logoColor=white" alt="Github" />
</a>
<a href="https://www.mediasfu.com/">
<img src="https://img.shields.io/badge/Website-4285F4?style=for-the-badge&logo=google-chrome&logoColor=white" alt="Website" />
</a>
</p>
<p align="center">
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square" alt="License: MIT" />
</a>
<a href="https://www.npmjs.com/package/mediasfu-reactjs">
<img src="https://img.shields.io/npm/v/mediasfu-reactjs.svg?style=flat-square" alt="npm version" />
</a>
<a href="https://reactjs.org">
<img src="https://img.shields.io/badge/React-18%20%7C%2019-61DAFB?style=flat-square&logo=react&logoColor=white" alt="React 18 | 19" />
</a>
<a href="https://www.typescriptlang.org">
<img src="https://img.shields.io/badge/TypeScript-Ready-007ACC?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" />
</a>
</p>
# MediaSFU ReactJS SDK
**Build production-ready video conferencing, webinars, broadcasts, and chat in minutes.**
MediaSFU ReactJS provides prebuilt, fully-featured event room components with real-time video/audio, screen sharing, recording, chat, polls, whiteboards, and more — ready to drop into your React app.
## ⚠️ Important: Backend Server Required
**MediaSFU is a frontend SDK that requires a backend media server to function.**
You have two options:
| Option | Description | Best For |
|--------|-------------|----------|
| **☁️ MediaSFU Cloud** | Managed service at [mediasfu.com](https://www.mediasfu.com) | Production apps, zero infrastructure |
| **🏠 MediaSFU Open** | Self-hosted open-source server | Full control, on-premise requirements |
```bash
# Option 1: Use MediaSFU Cloud
# Sign up at https://www.mediasfu.com and get your API credentials
# Option 2: Self-host with MediaSFU Open
git clone https://github.com/MediaSFU/MediaSFUOpen
cd MediaSFUOpen
```
📖 **[MediaSFU Cloud Documentation →](https://www.mediasfu.com/documentation#rooms)**
📖 **[MediaSFU Open Repository →](https://github.com/MediaSFU/MediaSFUOpen)**
## ✨ Platform Features
MediaSFU delivers enterprise-grade real-time communication with these core capabilities:
### 🎥 **Video & Audio**
- Multi-party video conferencing with adaptive quality
- Screen sharing with real-time annotation
- Virtual backgrounds and video effects
- Audio-only participant support
### 🎤 **Advanced Audio Features**
- **Real-time Translation** — Listen and speak in any language with live AI translation
- Automatic echo cancellation and noise suppression
- Spatial audio support
### 👥 **Participant Management**
- **Panelists Mode** — Designate speakers in webinars with audience Q&A
- **Individual Permissions** — Granular control per-participant (video/audio/screen/chat)
- **Co-host Delegation** — Share moderation duties with configurable responsibilities
- Waiting room with manual admit
- Breakout rooms for focused discussions
### 📊 **Engagement Tools**
- Live polls with real-time results
- In-meeting chat (direct & group)
- Collaborative whiteboards
- Reactions and hand raising
### 🎬 **Recording & Analytics**
- Cloud recording with track-based customization
- Watermarks, name tags, custom backgrounds
- Real-time call analytics
### 🔒 **Security & Control**
- End-to-end encryption option
- Managed events with time/capacity limits
- Abandoned participant handling
## 📖 Table of Contents
- [Quick Start](#-quick-start)
- [Installation](#-installation)
- [Prebuilt Event Rooms](#-prebuilt-event-rooms)
- [Usage Examples](#-usage-examples)
- [Key Components](#-key-components)
- [Customization](#-customization)
- [API Reference](#-api-reference)
- [Self-Hosting / Community Edition](#-self-hosting--community-edition)
- [Modern UI Components](#-modern-ui-components)
- [Advanced Features](#-advanced-features) *(Panelists, Permissions, Translation)*
- [sourceParameters - The Power API](#-sourceparameters---the-power-api)
- [AudioGrid - Display All Audio Participants](#-audiogrid---display-all-audio-participants)
- [Using Modals Standalone](#-using-modals-standalone)
- [Building Your Own UI](#-building-your-own-ui)
- [Detailed Documentation](#-detailed-documentation)
## 🚀 Quick Start
```bash
npm install mediasfu-reactjs
```
```tsx
import { MediasfuGeneric } from 'mediasfu-reactjs';
function App() {
return (
<MediasfuGeneric
credentials={{ apiUserName: "yourUsername", apiKey: "yourAPIKey" }}
/>
);
}
```
**That's it!** You now have a fully-featured video conferencing room.
> ℹ️ Critical component styles are automatically injected at runtime. For additional styling options, see [Optional CSS Import](#optional-css-import).
## 📦 Installation
```bash
# npm
npm install mediasfu-reactjs
# yarn
yarn add mediasfu-reactjs
# pnpm
pnpm add mediasfu-reactjs
```
### Peer Dependencies
The following are required peer dependencies:
```json
{
"react": "^18.2.0 || ^19.0.0",
"react-dom": "^18.2.0 || ^19.0.0",
"@fortawesome/fontawesome-svg-core": "^6.0.0",
"@fortawesome/free-solid-svg-icons": "^6.0.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"bootstrap": "^5.0.0",
"mediasoup-client": "^3.7.0",
"socket.io-client": "^4.0.0",
"universal-cookie": "^7.0.0"
}
```
### Optional Peer Dependencies
For **virtual background** support (blur, image backgrounds):
```bash
npm install @mediapipe/selfie_segmentation@0.1.1675465747
```
This is optional — if not installed, virtual backgrounds simply won't be available.
### Optional CSS Import
Critical styles for control buttons, containers, and core components are **automatically injected** at runtime — no manual import needed.
If you need additional styling (e.g., custom modal themes, waiting room lists), you can optionally import the full stylesheet:
```tsx
// Optional: Import for additional component styles
import 'mediasfu-reactjs/dist/main.css';
```
Most applications work perfectly without this import.
## 🏛️ Prebuilt Event Rooms
| Component | Use Case | Description |
|-----------|----------|-------------|
| `MediasfuGeneric` | **Universal** | Supports all event types dynamically |
| `MediasfuConference` | **Meetings** | Multi-party video conferencing |
| `MediasfuWebinar` | **Webinars** | Presenters + audience model |
| `MediasfuBroadcast` | **Broadcasting** | One-to-many live streaming |
| `MediasfuChat` | **Chat Rooms** | Text-based with optional media |
All prebuilt components share the same props interface:
```tsx
interface MediasfuProps {
// Authentication
credentials?: { apiUserName: string; apiKey: string };
// Connection
localLink?: string; // Self-hosted server URL
connectMediaSFU?: boolean; // Toggle auto-connection
// Customization
PrejoinPage?: (options) => ReactNode;
customVideoCard?: CustomVideoCardType;
customAudioCard?: CustomAudioCardType;
customMiniCard?: CustomMiniCardType;
uiOverrides?: MediasfuUICustomOverrides;
// Advanced
returnUI?: boolean; // Set false for headless mode
useLocalUIMode?: boolean; // Demo/local mode
seedData?: SeedData; // Pre-populate for demos
}
```
## 💡 Usage Examples
### Basic Conference Room
```tsx
import { MediasfuConference } from 'mediasfu-reactjs';
function ConferenceApp() {
return (
<MediasfuConference
credentials={{
apiUserName: "yourUsername",
apiKey: "yourAPIKey"
}}
/>
);
}
```
### Webinar with Custom Branding
```tsx
import { MediasfuWebinar, PreJoinPage } from 'mediasfu-reactjs';
function WebinarApp() {
return (
<MediasfuWebinar
credentials={{ apiUserName: "user", apiKey: "key" }}
PrejoinPage={(options) => (
<PreJoinPage
{...options}
imgSrc="/your-logo.png"
/>
)}
containerStyle={{
background: "linear-gradient(135deg, #1a1a2e, #16213e)"
}}
/>
);
}
```
### Demo/Preview Mode (No Server)
```tsx
import {
MediasfuGeneric,
generateRandomParticipants,
generateRandomMessages
} from 'mediasfu-reactjs';
function DemoApp() {
return (
<MediasfuGeneric
useLocalUIMode={true}
useSeed={true}
seedData={{
member: "DemoUser",
participants: generateRandomParticipants({ count: 5 }),
messages: generateRandomMessages({ count: 10 }),
eventType: "conference"
}}
/>
);
}
```
### Custom Video Cards
```tsx
import { MediasfuGeneric, VideoCard, CustomVideoCardType } from 'mediasfu-reactjs';
const customVideoCard: CustomVideoCardType = (props) => (
<VideoCard
{...props}
customStyle={{
border: "3px solid #4c1d95",
borderRadius: 20,
boxShadow: "0 10px 40px rgba(76, 29, 149, 0.4)"
}}
/>
);
function App() {
return (
<MediasfuGeneric
credentials={{ apiUserName: "user", apiKey: "key" }}
customVideoCard={customVideoCard}
/>
);
}
```
### Headless Mode (Custom UI)
```tsx
import { MediasfuGeneric } from 'mediasfu-reactjs';
import { useState, useCallback } from 'react';
function CustomApp() {
const [helpers, setHelpers] = useState<Record<string, unknown>>({});
const updateSourceParameters = useCallback((data: Record<string, unknown>) => {
setHelpers(data);
}, []);
return (
<>
<MediasfuGeneric
credentials={{ apiUserName: "user", apiKey: "key" }}
returnUI={false} // No default UI
noUIPreJoinOptions={{
action: "create",
capacity: 10,
eventType: "conference",
userName: "Host"
}}
sourceParameters={helpers}
updateSourceParameters={updateSourceParameters}
/>
{/* Use `helpers` to build your completely custom UI */}
{/* helpers.clickVideo(), helpers.clickAudio(), helpers.participants, etc. */}
</>
);
}
```
### UI Overrides
```tsx
import {
MediasfuGeneric,
MainContainerComponent,
MediasfuUICustomOverrides
} from 'mediasfu-reactjs';
const overrides: MediasfuUICustomOverrides = {
// Override component rendering
mainContainer: {
render: (props) => (
<div style={{ border: "4px dashed purple", padding: 16 }}>
<MainContainerComponent {...props} />
</div>
)
},
// Wrap function behavior
consumerResume: {
wrap: (originalFn) => async (params) => {
console.log("Consumer resuming:", params);
return await originalFn(params);
}
}
};
function App() {
return (
<MediasfuGeneric
credentials={{ apiUserName: "user", apiKey: "key" }}
uiOverrides={overrides}
/>
);
}
```
## 🧩 Key Components
### Display Components
| Component | Purpose |
|-----------|---------|
| `MainContainerComponent` | Root container for meeting UI |
| `MainGridComponent` | Grid layout for video tiles |
| `FlexibleGrid` | Dynamic responsive grid |
| `VideoCard` | Individual video participant |
| `AudioCard` | Audio-only participant with waveform |
| `MiniCard` | Thumbnail participant card |
| `Pagination` | Navigate participant pages |
### Modal Components
| Component | Purpose |
|-----------|---------|
| `LoadingModal` | Loading overlay |
| `AlertComponent` | Toast notifications |
| `ParticipantsModal` | Participant list/management |
| `MessagesModal` | Chat interface |
| `RecordingModal` | Recording controls |
| `PollModal` | Create/vote on polls |
| `BackgroundModal` | Virtual backgrounds |
| `BreakoutRoomsModal` | Breakout room management |
| `ConfigureWhiteboardModal` | Whiteboard settings |
| `MediaSettingsModal` | Audio/video device selection |
### Control Methods
```tsx
import {
clickVideo, // Toggle video
clickAudio, // Toggle audio
clickScreenShare, // Toggle screen share
startRecording, // Start recording
stopRecording, // Stop recording
launchPoll, // Open poll modal
launchMessages, // Open chat modal
} from 'mediasfu-reactjs';
```
### Socket Events
```tsx
import {
connectSocket,
disconnectSocket,
joinRoomClient,
// Event handlers
personJoined,
meetingEnded,
receiveMessage,
} from 'mediasfu-reactjs';
```
## 🎨 Customization
### CSS Variables
The package uses CSS variables for theming:
```css
:root {
--mediasfu-primary: #4c1d95;
--mediasfu-background: #1a1a2e;
--mediasfu-surface: #16213e;
--mediasfu-text: #ffffff;
}
```
### Custom Cards
```tsx
// Custom video card with overlay
const customVideoCard: CustomVideoCardType = (props) => (
<div style={{ position: 'relative' }}>
<VideoCard {...props} />
<div className="custom-overlay">
<span>{props.name}</span>
<button onClick={() => props.onMute?.()}>Mute</button>
</div>
</div>
);
```
### Complete UI Override
```tsx
const overrides: MediasfuUICustomOverrides = {
// Layout components
mainContainer: { render: CustomMainContainer },
mainGrid: { render: CustomGrid },
// Modal components
menuModal: { component: CustomMenuModal },
participantsModal: { component: CustomParticipantsModal },
messagesModal: { component: CustomMessagesModal },
// Functions
consumerResume: { wrap: loggingWrapper },
addVideosGrid: { implementation: customGridLogic },
};
```
## 📚 API Reference
### Types
```tsx
import type {
// Core types
Participant,
Stream,
Message,
CoHostResponsibility,
Poll,
// Event types
EventType, // 'conference' | 'webinar' | 'chat' | 'broadcast'
CreateMediaSFURoomOptions,
JoinMediaSFURoomOptions,
ResponseJoinRoom,
// UI types
MediasfuUICustomOverrides,
CustomVideoCardType,
CustomAudioCardType,
CustomMiniCardType,
// Socket types
ConnectSocketType,
ConnectLocalSocketType,
} from 'mediasfu-reactjs';
```
### Utilities
```tsx
import {
// Room management
joinRoomOnMediaSFU,
createRoomOnMediaSFU,
checkLimitsAndMakeRequest,
// Demo utilities
generateRandomParticipants,
generateRandomMessages,
generateRandomPolls,
// State
initialValuesState,
// Helpers
formatNumber,
sleep,
checkPermission,
} from 'mediasfu-reactjs';
```
## 🏠 Self-Hosting / Community Edition
For self-hosted MediaSFU servers:
```tsx
<MediasfuGeneric
localLink="https://your-mediasfu-server.com"
connectMediaSFU={false} // Don't connect to cloud
/>
```
### Hybrid Mode (Local + Cloud)
```tsx
<MediasfuGeneric
localLink="https://your-server.com"
connectMediaSFU={true} // Also connect to MediaSFU cloud
credentials={{ apiUserName: "user", apiKey: "key" }}
/>
```
## 📖 Detailed Documentation
For comprehensive documentation including:
- Advanced customization patterns
- Full API reference
- All component props
- Socket event handling
- Recording configuration
- Breakout rooms
- Whiteboard integration
- And much more...
**📄 See [README_DETAILED.md](./README_DETAILED.md)**
## 🎨 Modern UI Components
`ModernMediasfuGeneric` is the most advanced, themed variant featuring:
- **Premium glass-morphism design** with backdrop blur effects
- **Smooth animations** and micro-interactions
- **Dark/Light theme** support built-in
- **Accessibility-first** components
- **Responsive layouts** for all screen sizes
```tsx
import { ModernMediasfuGeneric } from 'mediasfu-reactjs';
function App() {
return (
<ModernMediasfuGeneric
credentials={{ apiUserName: "user", apiKey: "key" }}
containerStyle={{
background: "linear-gradient(135deg, #0f172a, #1e3a8a)",
minHeight: "100vh"
}}
/>
);
}
```
### Modern Components Available
| Modern Component | Classic Equivalent | Features |
|-----------------|-------------------|----------|
| `ModernVideoCard` | `VideoCard` | Glass effect, animated borders |
| `ModernAudioCard` | `AudioCard` | Gradient waveforms, glow effects |
| `ModernMiniCard` | `MiniCard` | Sleek thumbnails with status |
| `ModernMenuModal` | `MenuModal` | Slide animations, blur backdrop |
| `ModernMessagesModal` | `MessagesModal` | Chat bubbles, typing indicators |
| `ModernRecordingModal` | `RecordingModal` | Status animations, progress rings |
| `ModernParticipantsModal` | `ParticipantsModal` | Search, filters, role badges |
| `ModernBackgroundModal` | `BackgroundModal` | Image gallery, blur previews |
| `ModernPollModal` | `PollModal` | Real-time voting, animations |
| `ModernBreakoutRoomsModal` | `BreakoutRoomsModal` | Drag-and-drop, room previews |
| `ModernPanelistsModal` | `PanelistsModal` | Panelist management for webinars |
| `ModernPermissionsModal` | `PermissionsModal` | Per-participant permission control |
| `TranslationSettingsModal` | — | Real-time translation configuration |
## 🌐 Advanced Features
### Panelists Mode (Webinars)
In webinar mode, designate specific participants as **panelists** who can speak, while others remain audience members.
```tsx
// Panelists are managed via sourceParameters
const { panelists, updatePanelists } = sourceParameters;
// Listen for panelist changes
// Events: panelistsUpdated, addedAsPanelist, removedFromPanelists, panelistFocusChanged
```
### Individual Permissions
Control each participant's capabilities individually:
```tsx
import { ModernPermissionsModal } from 'mediasfu-reactjs';
// Permission levels:
// "0" - Standard participant
// "1" - Elevated (co-host level)
// "2" - Host (full control)
// Configure per-participant capabilities:
// - Video on/off
// - Audio on/off
// - Screen sharing
// - Chat access
```
### Real-time Translation 🌍
Enable participants to **speak in their native language** and **listen in any language** with live AI translation.
```tsx
import { TranslationSettingsModal } from 'mediasfu-reactjs';
function TranslationExample({ sourceParameters }) {
const [showTranslation, setShowTranslation] = useState(false);
return (
<>
<button onClick={() => setShowTranslation(true)}>
🌐 Translation Settings
</button>
<TranslationSettingsModal
isVisible={showTranslation}
onClose={() => setShowTranslation(false)}
parameters={sourceParameters}
/>
</>
);
}
// Translation events available:
// - translation:roomConfig
// - translation:languageSet
// - translation:subscribed
// - translation:transcript
```
Features include:
- **Set your spoken language** — The system knows what language you're speaking
- **Choose listening language** — Hear others translated to your preferred language
- **Real-time transcription** — See live transcripts
- **Multiple language support** — 50+ languages available
## 🔧 sourceParameters - The Power API
When building custom UIs or using headless mode (`returnUI={false}`), you need **both** props:
| Prop | Purpose |
|------|---------|
| `sourceParameters` | Initial state object (can be empty `{}`) |
| `updateSourceParameters` | Callback that receives the complete helper bundle |
The `updateSourceParameters` callback delivers a comprehensive object containing **all room state, methods, and streams**. This is your bridge to building completely custom UIs.
```tsx
import { MediasfuGeneric } from 'mediasfu-reactjs';
import { useState, useCallback } from 'react';
function CustomUI() {
const [helpers, setHelpers] = useState<Record<string, unknown>>({});
// This callback receives ALL MediaSFU state and methods
const updateSourceParameters = useCallback((data: Record<string, unknown>) => {
setHelpers(data);
}, []);
return (
<>
<MediasfuGeneric
credentials={{ apiUserName: "user", apiKey: "key" }}
returnUI={false} // Headless mode - no UI rendered
noUIPreJoinOptions={{
action: "create",
capacity: 10,
eventType: "conference",
userName: "Host"
}}
sourceParameters={helpers} // Required: pass state object
updateSourceParameters={updateSourceParameters} // Required: receive updates
/>
{helpers.validated && <MyCustomMeetingUI sourceParameters={helpers} />}
</>
);
}
```
### Key sourceParameters Properties
#### Media Streams
```tsx
const {
allVideoStreams, // All video MediaStream objects
allAudioStreams, // All audio MediaStream objects
localStream, // Your local camera stream
localStreamAudio, // Your local microphone stream
localStreamScreen, // Your screen share stream (if active)
remoteScreenStream, // Remote screen share stream
} = sourceParameters;
```
#### Participant Data
```tsx
const {
participants, // Full participant list
participantsCounter, // Current count
filteredParticipants, // Filtered by search
waitingRoomList, // Users in waiting room
coHost, // Current co-host name
member, // Your username
islevel, // Your permission level ('0', '1', '2')
youAreHost, // Boolean
youAreCoHost, // Boolean
} = sourceParameters;
```
#### Room State
```tsx
const {
roomName, // Current room name
eventType, // 'conference' | 'webinar' | 'broadcast' | 'chat'
recordStarted, // Is recording active
recordPaused, // Is recording paused
shareScreenStarted, // Is screen sharing active
validated, // Is room validated/connected
messages, // Chat messages array
polls, // Active polls
} = sourceParameters;
```
### Helper Methods
#### `getParticipantMedia` - Get Individual Streams
```tsx
const { getParticipantMedia } = sourceParameters;
// Get video stream by participant name
const videoStream = await getParticipantMedia({
name: "Alice",
kind: "video"
});
// Get audio stream by producer ID
const audioStream = await getParticipantMedia({
id: "producer-id-123",
kind: "audio"
});
// Use in a video element
if (videoStream) {
videoRef.current.srcObject = videoStream;
}
```
#### Media Control Methods
```tsx
const {
clickVideo, // Toggle local video
clickAudio, // Toggle local audio
clickScreenShare, // Toggle screen share
switchVideoAlt, // Switch camera device
switchUserAudio, // Switch audio input device
} = sourceParameters;
// Toggle video with parameters
await clickVideo({ parameters: sourceParameters });
```
#### Modal Toggles
```tsx
const {
updateIsMenuModalVisible,
updateIsRecordingModalVisible,
updateIsParticipantsModalVisible,
updateIsMessagesModalVisible,
updateIsPollModalVisible,
updateIsBackgroundModalVisible,
updateIsBreakoutRoomsModalVisible,
updateIsMediaSettingsModalVisible,
} = sourceParameters;
// Open the chat modal
updateIsMessagesModalVisible(true);
```
## 🎵 AudioGrid - Display All Audio Participants
Use `AudioGrid` with audio streams from `sourceParameters`:
```tsx
import { AudioGrid, AudioCard } from 'mediasfu-reactjs';
function AudioParticipantsView({ sourceParameters }) {
const { allAudioStreams, participants } = sourceParameters;
// Build audio components from streams
const audioComponents = allAudioStreams.map((streamObj, index) => {
const participant = participants.find(p => p.audioID === streamObj.producerId);
return (
<AudioCard
key={streamObj.producerId || index}
name={participant?.name || 'Unknown'}
audioStream={streamObj.stream}
showWaveform={true}
barColor="#22c55e"
customStyle={{
background: "rgba(34, 197, 94, 0.1)",
borderRadius: 16,
padding: 12
}}
/>
);
});
return (
<AudioGrid
componentsToRender={audioComponents}
containerProps={{
style: {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
gap: 16,
padding: 20
}
}}
/>
);
}
```
## 🎬 Using Modals Standalone
You can use any modal component independently with `sourceParameters`:
### Recording Modal
```tsx
import { ModernRecordingModal } from 'mediasfu-reactjs';
function MyRecordingButton({ sourceParameters }) {
const [showRecording, setShowRecording] = useState(false);
return (
<>
<button onClick={() => setShowRecording(true)}>
🔴 Recording Settings
</button>
<ModernRecordingModal
isVisible={showRecording}
onClose={() => setShowRecording(false)}
parameters={sourceParameters}
position="center"
/>
</>
);
}
```
### Virtual Background Modal
```tsx
import { ModernBackgroundModal } from 'mediasfu-reactjs';
function BackgroundSelector({ sourceParameters }) {
const [showBg, setShowBg] = useState(false);
return (
<>
<button onClick={() => setShowBg(true)}>
🖼️ Change Background
</button>
<ModernBackgroundModal
isVisible={showBg}
onClose={() => setShowBg(false)}
parameters={sourceParameters}
/>
</>
);
}
```
### Media Settings Modal
```tsx
import { ModernMediaSettingsModal } from 'mediasfu-reactjs';
function DeviceSelector({ sourceParameters }) {
const [showSettings, setShowSettings] = useState(false);
return (
<>
<button onClick={() => setShowSettings(true)}>
⚙️ Audio/Video Settings
</button>
<ModernMediaSettingsModal
isVisible={showSettings}
onClose={() => setShowSettings(false)}
parameters={sourceParameters}
/>
</>
);
}
```
## 🏗️ Building Your Own UI
Here's a complete example of building a custom meeting interface:
```tsx
import {
MediasfuGeneric,
VideoCard,
AudioCard,
ModernMessagesModal,
ModernParticipantsModal,
ModernRecordingModal,
} from 'mediasfu-reactjs';
import { useState, useEffect } from 'react';
function CustomMeetingApp() {
const [params, setParams] = useState<any>(null);
const [showChat, setShowChat] = useState(false);
const [showParticipants, setShowParticipants] = useState(false);
const [showRecording, setShowRecording] = useState(false);
if (!params?.validated) {
return (
<MediasfuGeneric
credentials={{ apiUserName: "user", apiKey: "key" }}
returnUI={true} // Show pre-join UI
updateSourceParameters={setParams}
/>
);
}
const {
participants,
allVideoStreams,
allAudioStreams,
member,
roomName,
clickVideo,
clickAudio,
clickScreenShare,
videoAlreadyOn,
audioAlreadyOn,
shareScreenStarted,
recordStarted,
} = params;
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh', background: '#0f172a' }}>
{/* Header */}
<header style={{ padding: 16, borderBottom: '1px solid #334155', display: 'flex', justifyContent: 'space-between' }}>
<h1 style={{ color: 'white', margin: 0 }}>{roomName}</h1>
<span style={{ color: '#94a3b8' }}>{participants.length} participants</span>
</header>
{/* Video Grid */}
<main style={{ flex: 1, display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 16, padding: 16 }}>
{allVideoStreams.map((stream, i) => {
const participant = participants.find(p => p.videoID === stream.producerId);
return (
<VideoCard
key={stream.producerId || i}
videoStream={stream.stream}
name={participant?.name || 'Unknown'}
customStyle={{ borderRadius: 16, overflow: 'hidden' }}
/>
);
})}
</main>
{/* Controls */}
<footer style={{ padding: 16, borderTop: '1px solid #334155', display: 'flex', justifyContent: 'center', gap: 16 }}>
<button
onClick={() => clickVideo({ parameters: params })}
style={{
padding: '12px 24px',
borderRadius: 12,
background: videoAlreadyOn ? '#22c55e' : '#ef4444',
color: 'white',
border: 'none',
cursor: 'pointer'
}}
>
{videoAlreadyOn ? '📹 Video On' : '📹 Video Off'}
</button>
<button
onClick={() => clickAudio({ parameters: params })}
style={{
padding: '12px 24px',
borderRadius: 12,
background: audioAlreadyOn ? '#22c55e' : '#ef4444',
color: 'white',
border: 'none',
cursor: 'pointer'
}}
>
{audioAlreadyOn ? '🎤 Mic On' : '🎤 Mic Off'}
</button>
<button onClick={() => setShowParticipants(true)} style={{ padding: '12px 24px', borderRadius: 12, background: '#3b82f6', color: 'white', border: 'none', cursor: 'pointer' }}>
👥 Participants
</button>
<button onClick={() => setShowChat(true)} style={{ padding: '12px 24px', borderRadius: 12, background: '#8b5cf6', color: 'white', border: 'none', cursor: 'pointer' }}>
💬 Chat
</button>
<button onClick={() => setShowRecording(true)} style={{ padding: '12px 24px', borderRadius: 12, background: recordStarted ? '#ef4444' : '#6b7280', color: 'white', border: 'none', cursor: 'pointer' }}>
🔴 Record
</button>
</footer>
{/* Modals */}
<ModernMessagesModal
isVisible={showChat}
onClose={() => setShowChat(false)}
parameters={params}
/>
<ModernParticipantsModal
isVisible={showParticipants}
onClose={() => setShowParticipants(false)}
parameters={params}
/>
<ModernRecordingModal
isVisible={showRecording}
onClose={() => setShowRecording(false)}
parameters={params}
/>
</div>
);
}
```
## 🔗 Links
- **Website**: [mediasfu.com](https://www.mediasfu.com)
- **Documentation**: [mediasfu.com/documentation](https://www.mediasfu.com/documentation#rooms)
- **MediaSFU Open**: [github.com/MediaSFU/MediaSFUOpen](https://github.com/MediaSFU/MediaSFUOpen)
- **Community Forum**: [mediasfu.com/forums](https://www.mediasfu.com/forums)
- **GitHub**: [github.com/MediaSFU](https://github.com/MediaSFU)
## 📄 License
MIT © [MediaSFU](https://www.mediasfu.com)
<p align="center">
<strong>Built with ❤️ by MediaSFU</strong>
</p>