Embed MediaSFU widgets
MediaSFU widgets are focused embeddable surfaces for calls, meetings, agents, and operator workflows. Your application remains responsible for identity, authorization, navigation, branding, and business state.
Install the published @mediasfu/widgets@0.1.0 package from npm, or use the
production CDN for a plain-script integration. The package supports
mediasfu-reactjs >=4.3.0 <5.
Build this: compare the widgets with other runnable options in the
Starter Projects catalog. View source: open the public
mediasfu-widgets repository.
The package, public exports, types, local tests, and production widget-shell delivery have been validated. That does not by itself prove every carrier call, agent conversation, two-party media session, visual state, or teardown path in your application; test the complete flow with your own backend, roles, browser, and devices.
Choose a surface
| Surface | Custom element | Package subpath | Use it for |
|---|---|---|---|
| Call Button | <mediasfu-call-button> | @mediasfu/widgets/call-button | Click-to-call, SIP, or PSTN entry points |
| Meeting Join | <mediasfu-meeting-join> | @mediasfu/widgets/meeting-join | Secure room creation and joining |
| AI Agent | <mediasfu-ai-agent> | @mediasfu/widgets/ai-agent | Voice and multimodal agent sessions |
| Web Agent | <mediasfu-web-agent> | @mediasfu/widgets/web-agent | Text, voice, multimodal support, and escalation |
| Calls Dashboard | <mediasfu-calls> | @mediasfu/widgets/calls | Authorized call operations and history |
| Agent Dashboard | <mediasfu-agent-dashboard> | @mediasfu/widgets/agent-dashboard | Agent monitoring and operator takeover |
Widget Builder and Widget Studio configure these surfaces; they are product surfaces, not additional package elements. See Widget Builder, Widget Studio, and AI Studio.
Install and load
npm install @mediasfu/widgets
Import only the element your application uses:
import '@mediasfu/widgets/call-button';
import '@mediasfu/widgets/meeting-join';
import '@mediasfu/widgets/ai-agent';
import '@mediasfu/widgets/web-agent';
import '@mediasfu/widgets/calls';
import '@mediasfu/widgets/agent-dashboard';
Keep only the required import in a real application. Modular subpaths avoid loading unrelated widget entry modules. Measure the built artifact in your own bundler when an exact package-size budget matters; this guide does not promise a fixed compressed size.
For a static site or CMS, load the production bundle once. It registers all six custom elements:
<script type="module" src="https://cdn.mediasfu.com/v1/widget.js"></script>
The CDN bundle is convenient when a page may render several surfaces. Prefer a modular npm subpath when the application needs only one element, so unrelated widget code is not included in its bundle.
Create the public, domain-scoped widget-key in Widget Builder and restrict it
to the sites that may embed the element. It is not a MediaSFU API key.
Call Button
@mediasfu/widgets/call-button exports MediaSFUCallButton and registers
mediasfu-call-button. It starts a product-authorized click-to-call flow.
Destination, caller identity, and any SIP/PSTN bridge remain backend decisions;
the element is not a telephony credential or a promise of live carrier service.
import '@mediasfu/widgets/call-button';
<mediasfu-call-button
widget-key="YOUR_PUBLIC_WIDGET_KEY"
button-text="Call support"
button-icon="headset"
theme="auto"
require-name="true"
></mediasfu-call-button>
Listen for call-ringing, call-connected, call-ended, call-failed,
audio-unlock-required, microphone-error, and widget-error. Ringing is
call progress; call-connected is the connected signal.
Meeting Join
@mediasfu/widgets/meeting-join exports MediaSFUMeetingJoin and registers
mediasfu-meeting-join. Send the room request to your authenticated backend,
which creates or joins the room and returns a short-lived, scoped result. Use a
retry-scoped idempotency key so a repeated create request cannot create an
unintended second room.
import '@mediasfu/widgets/meeting-join';
<mediasfu-meeting-join
widget-key="YOUR_PUBLIC_WIDGET_KEY"
mode="both"
room-prefix="consultation-"
require-name="true"
show-preview="true"
show-device-settings="true"
redirect-mode="iframe"
></mediasfu-meeting-join>
mode accepts join-only, create-only, or both; redirect-mode accepts
new-tab or iframe. The preview can enumerate and switch cameras and
microphones, display microphone level, and run a short playback test. Listen
for meeting-join, meeting-start, meeting-end, and widget-error.
AI Agent
@mediasfu/widgets/ai-agent exports MediaSFUAIAgent and registers
mediasfu-ai-agent. Configure the approved agent and presentation mode in the
host application. Voice, multimodal input, provider credentials, and escalation
to a human are governed by the authorized backend and the agent configuration;
the widget shell alone does not prove an agent conversation or media session.
import '@mediasfu/widgets/ai-agent';
<mediasfu-ai-agent
widget-key="YOUR_PUBLIC_WIDGET_KEY"
agent-id="YOUR_CONFIGURED_AGENT_ID"
mode="voice"
theme="dark"
width="100%"
height="700px"
></mediasfu-ai-agent>
Listen for agent-loaded, ai-session-start, agent-disconnect, and
widget-error. sendCommand(command, data) sends a bounded command to the
owned frame; it does not grant additional agent permissions.
Web Agent
@mediasfu/widgets/web-agent exports MediaSFUWebAgent and registers
mediasfu-web-agent. It supports a web-facing agent surface and bounded
message, session, and escalation actions. Human handoff must be an authorized
backend transition with an operator policy, not an unauthenticated browser
command.
import '@mediasfu/widgets/web-agent';
<mediasfu-web-agent
widget-key="YOUR_PUBLIC_WIDGET_KEY"
config-name="customer-support"
mode="text"
allow-escalation="true"
greeting="How can we help?"
width="100%"
height="680px"
></mediasfu-web-agent>
The public methods are startSession(), sendMessage(message),
requestEscalation(), sendCommand(command, data), and setTheme(theme).
Listen for web-agent-ready, session-started, message-received,
escalation-requested, session-ended, and widget-error. An escalation
event requests a human; an authorized operator completes the handoff.
Calls Dashboard
@mediasfu/widgets/calls exports MediaSFUCalls and registers
mediasfu-calls. Use it for authenticated call operations and history. Call
records, destinations, and operator actions come from the backend contract and
the account entitlement; a dashboard import does not grant access to them.
import '@mediasfu/widgets/calls';
<mediasfu-calls
widget-key="YOUR_PUBLIC_WIDGET_KEY"
theme="dark"
width="100%"
height="720px"
></mediasfu-calls>
Listen for calls-loaded, call-start, call-connected, call-ended,
incoming-call, active-calls-changed, hold-changed, navigate-request,
and widget-error.
Agent Dashboard
@mediasfu/widgets/agent-dashboard exports MediaSFUAgentDashboard and
registers mediasfu-agent-dashboard. It is for authorized monitoring and
operator takeover. Keep operator grants short-lived, scoped, and delivered
only after the embedded session has established its exact origin.
import '@mediasfu/widgets/agent-dashboard';
<mediasfu-agent-dashboard
widget-key="YOUR_PUBLIC_WIDGET_KEY"
operator-name="Support team"
theme="dark"
width="100%"
height="760px"
></mediasfu-agent-dashboard>
Listen for dashboard-loaded, dashboard-ready,
active-sessions-changed, session-updated, operator-action,
operator-takeover, and widget-error. Takeover remains role-scoped; it does
not grant unrestricted room control.
React entry
@mediasfu/widgets/react is the opt-in React composition entry. Use it when
your application wants React components while retaining the widget's bounded
behavior. It is separate from the six custom-element subpaths and does not
turn a widget into the complete MediaSFU SDK.
npm install @mediasfu/widgets mediasfu-reactjs@^4.3 react react-dom @mediapipe/selfie_segmentation
import {MeetingJoinWidget} from '@mediasfu/widgets/react';
export function ConsultationJoin({roomCode, displayName}) {
return (
<MeetingJoinWidget
meetingID={roomCode}
userName={displayName}
showPreview
showAudioDeviceSelect
showVideoDeviceSelect
/>
);
}
Keep durable credentials out of component props in a distributed application. Authorize the session on your backend and pass only its scoped result.
Headless entry
@mediasfu/widgets/headless exposes semantic state and actions for an
application-owned renderer. Your application owns layout, labels,
accessibility, theming, and media renderer placement. Mount every prepared
remote-audio renderer exposed by the state, not only the video cards currently
visible on screen.
import {useMediaSFU} from '@mediasfu/widgets/headless';
export function RoomControls() {
const {session, media, permissions, actions} = useMediaSFU();
return (
<section aria-label="Call controls">
<p>{session.status}</p>
<button
disabled={!permissions.canUseMicrophone}
onClick={() => void actions.toggleMicrophone()}
>
{media.microphone.active ? 'Mute' : 'Unmute'}
</button>
<button onClick={() => void actions.leave()}>Leave</button>
</section>
);
}
Render this beneath one MediaSFUProvider. The semantic state deliberately
omits sockets, streams, credentials, and mutable SDK parameter bags. Your app
owns pending/error states, rendering, accessibility, reconnect feedback, and
cleanup. Direct reusable credentials are restricted-development inputs; use a
backend-authorized integration before distributing the application.
Attributes, events, and styling
Supported configuration is deliberately bounded to presentation and product flow. Call Button accepts destination and caller-display fields; Meeting Join accepts room prefix/code, preview, and participant fields; AI Agent and Web Agent accept approved agent or mode fields, dimensions, and brand color; the two dashboards accept dimensions, theme, brand color, and approved CSS hooks. Use the documented attributes or React props for these fields. Do not pass arbitrary SDK internals as widget configuration.
| Surface | Common attributes |
|---|---|
| Call Button | destination, caller-id, button-text, button-icon, theme, position, show-status, require-name, require-email, primary-color, text-color |
| Meeting Join | room-prefix, room-code, mode, redirect-mode, show-preview, show-device-settings, require-name, require-email, start-with-audio-off, start-with-video-off |
| AI Agent | agent-id, mode, theme, width, height, brand-color, idle-style |
| Web Agent | config-name, mode, allow-escalation, agent-name, greeting, theme, width, height, brand-color |
| Calls Dashboard | theme, width, height, brand-color, custom-css |
| Agent Dashboard | operator-name, theme, width, height, brand-color, custom-css-class, custom-css |
Boolean attributes use the strings "true" and "false". Widget events are
bubbling CustomEvent instances; read their bounded payload from
event.detail. Never use events as a credential channel.
You can theme the prebuilt surfaces with their theme and brand-color options and host CSS/custom CSS boundaries. Headless mode is the appropriate choice when the application owns the complete visual renderer. Give every embedded surface a measured width and height so responsive layout and remote media have an intentional container.
<section class="support-widget">
<mediasfu-web-agent
widget-key="YOUR_PUBLIC_WIDGET_KEY"
theme="auto"
brand-color="#2563eb"
width="100%"
height="min(720px, 82vh)"
></mediasfu-web-agent>
</section>
<style>
.support-widget {
width: min(100%, 960px);
margin-inline: auto;
}
</style>
Supply custom-css and content overrides only from trusted application or
Studio configuration; never interpolate unsanitized visitor input.
Plain script, modular imports, and package size
Static pages can use an approved plain-script bundle for the custom elements. Bundled applications should import only the needed modular subpath rather than the entire package:
import '@mediasfu/widgets/meeting-join';
React applications can import @mediasfu/widgets/react, and application-owned
renderers can import @mediasfu/widgets/headless. A plain script and a modular
import have different loading and release boundaries; choose the delivery
channel that your application can govern.
Responsive sizing
- Give meeting, agent, and dashboard surfaces a resolvable height.
- Prefer
width="100%"inside a container with a deliberate maximum width. - Test narrow mobile widths, landscape orientation, browser zoom, software keyboards, and safe-area insets.
- Avoid clipping permission prompts or audio-unlock controls with a short
parent that uses
overflow: hidden.
Secure sessions and iframe authorization
Keep MediaSFU API keys, agent/provider credentials, session grants, and backend secrets out of HTML, JavaScript bundles, URLs, logs, and referrers. A public widget key is domain-scoped and is not a reusable MediaSFU credential.
For an iframe-backed agent or dashboard, send a short-lived grant after the iframe loads through exact-origin, exact-window messaging. Validate the sender before accepting it, and remove listeners, timers, room state, and media when the surface is removed. The host backend owns create/join authority, entitlements, telephony authorization, and operator permissions.
SSR and cleanup
The public widget subpaths and React/headless entries can be imported during
server rendering. Begin browser authentication, device access, and media work
only after hydration; do not read window, navigator, or media devices while
rendering on the server. On unmount, stop listeners and timers and leave the
participant session. Only an authorized host action should end a room.
Removing a custom element releases its owned listeners, timers, frames, preview streams, and in-memory grants. Your app must remove its own listeners, revoke app-owned grants, and decide whether participant leave should preserve or end the room. Never reuse a previous user's room or operator state.
Common failures
| Symptom | What to check |
|---|---|
| The element never upgrades | The CDN script loaded once, or the correct npm subpath was imported before rendering |
widget-error appears immediately | The widget key exists, the current origin is allowed, HTTPS is in use, and the user is authorized |
| Camera or microphone does not start | Browser permission, selected device, operating-system privacy settings, and visible permission errors |
| Remote audio is silent | Browser autoplay may require a user gesture; expose the supplied audio-unlock action |
| Meeting cannot create or join | The backend-authorized response, room code, identity, retry key, and expiry |
| Agent cannot start | Agent configuration, provider availability, scoped session grant, and account authority |
| Handoff never completes | An authorized operator must accept it; an escalation event alone is not takeover |
| Dashboard rejects takeover | Refresh the signed-in operator grant and verify the assigned role |
| Frame is clipped | Give its host a resolvable width and height and remove conflicting overflow rules |
| SSR import works but the widget does not start | Begin authentication, device, iframe, and media work after hydration |
Cloud and MediaSFU Open
MediaSFU Cloud is the hosted service. MediaSFU Open is a media server that your organization operates. Open is not a hosted alternative endpoint: connect your authenticated backend and application to the server you operate, and keep its credentials on that server. Preserve the same browser identity, origin, and session-grant boundaries in either deployment.
Related product patterns
Use the package source, secure backend proxy, MediaSFU API details, telephony, agents, SpacesTek, meeting recipes, the sandbox, and the multi-SDK showcase patterns for the surrounding application composition. These examples describe product boundaries; they do not promote a widget-shell check into live telephony, agent-conversation, two-party media, visual, or end-to-end acceptance.