Skip to main content

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

SurfaceCustom elementPackage subpathUse it for
Call Button<mediasfu-call-button>@mediasfu/widgets/call-buttonClick-to-call, SIP, or PSTN entry points
Meeting Join<mediasfu-meeting-join>@mediasfu/widgets/meeting-joinSecure room creation and joining
AI Agent<mediasfu-ai-agent>@mediasfu/widgets/ai-agentVoice and multimodal agent sessions
Web Agent<mediasfu-web-agent>@mediasfu/widgets/web-agentText, voice, multimodal support, and escalation
Calls Dashboard<mediasfu-calls>@mediasfu/widgets/callsAuthorized call operations and history
Agent Dashboard<mediasfu-agent-dashboard>@mediasfu/widgets/agent-dashboardAgent 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.

SurfaceCommon attributes
Call Buttondestination, caller-id, button-text, button-icon, theme, position, show-status, require-name, require-email, primary-color, text-color
Meeting Joinroom-prefix, room-code, mode, redirect-mode, show-preview, show-device-settings, require-name, require-email, start-with-audio-off, start-with-video-off
AI Agentagent-id, mode, theme, width, height, brand-color, idle-style
Web Agentconfig-name, mode, allow-escalation, agent-name, greeting, theme, width, height, brand-color
Calls Dashboardtheme, width, height, brand-color, custom-css
Agent Dashboardoperator-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

SymptomWhat to check
The element never upgradesThe CDN script loaded once, or the correct npm subpath was imported before rendering
widget-error appears immediatelyThe widget key exists, the current origin is allowed, HTTPS is in use, and the user is authorized
Camera or microphone does not startBrowser permission, selected device, operating-system privacy settings, and visible permission errors
Remote audio is silentBrowser autoplay may require a user gesture; expose the supplied audio-unlock action
Meeting cannot create or joinThe backend-authorized response, room code, identity, retry key, and expiry
Agent cannot startAgent configuration, provider availability, scoped session grant, and account authority
Handoff never completesAn authorized operator must accept it; an escalation event alone is not takeover
Dashboard rejects takeoverRefresh the signed-in operator grant and verify the assigned role
Frame is clippedGive its host a resolvable width and height and remove conflicting overflow rules
SSR import works but the widget does not startBegin 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.

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.