mediasfu-reactjs
v4.2.3
Published
MediaSFU Prebuilt ReactJS SDK - Compatible with React 18 & 19, TypeScript & JavaScript
Downloads
148
Maintainers
Keywords
Readme
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 | Production apps, zero infrastructure | | 🏠 MediaSFU Open | Self-hosted open-source server | Full control, on-premise requirements |
# 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 →
📖 MediaSFU Open Repository →
✨ 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
- Installation
- Prebuilt Event Rooms
- Usage Examples
- Key Components
- Customization
- API Reference
- Self-Hosting / Community Edition
- Modern UI Components
- Advanced Features (Panelists, Permissions, Translation)
- sourceParameters - The Power API
- AudioGrid - Display All Audio Participants
- Using Modals Standalone
- Building Your Own UI
- Detailed Documentation
🚀 Quick Start
npm install mediasfu-reactjsimport { 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.
📦 Installation
# npm
npm install mediasfu-reactjs
# yarn
yarn add mediasfu-reactjs
# pnpm
pnpm add mediasfu-reactjsPeer Dependencies
The following are required peer dependencies:
{
"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):
npm install @mediapipe/[email protected]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:
// 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:
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
import { MediasfuConference } from 'mediasfu-reactjs';
function ConferenceApp() {
return (
<MediasfuConference
credentials={{
apiUserName: "yourUsername",
apiKey: "yourAPIKey"
}}
/>
);
}Webinar with Custom Branding
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)
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
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)
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
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
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
import {
connectSocket,
disconnectSocket,
joinRoomClient,
// Event handlers
personJoined,
meetingEnded,
receiveMessage,
} from 'mediasfu-reactjs';🎨 Customization
CSS Variables
The package uses CSS variables for theming:
:root {
--mediasfu-primary: #4c1d95;
--mediasfu-background: #1a1a2e;
--mediasfu-surface: #16213e;
--mediasfu-text: #ffffff;
}Custom Cards
// 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
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
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
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:
<MediasfuGeneric
localLink="https://your-mediasfu-server.com"
connectMediaSFU={false} // Don't connect to cloud
/>Hybrid Mode (Local + Cloud)
<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
🎨 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
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.
// Panelists are managed via sourceParameters
const { panelists, updatePanelists } = sourceParameters;
// Listen for panelist changes
// Events: panelistsUpdated, addedAsPanelist, removedFromPanelists, panelistFocusChangedIndividual Permissions
Control each participant's capabilities individually:
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 accessReal-time Translation 🌍
Enable participants to speak in their native language and listen in any language with live AI translation.
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:transcriptFeatures 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.
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
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
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
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
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
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
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:
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
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
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
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:
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
- Documentation: mediasfu.com/documentation
- MediaSFU Open: github.com/MediaSFU/MediaSFUOpen
- Community Forum: mediasfu.com/forums
- GitHub: github.com/MediaSFU
📄 License
MIT © MediaSFU
