npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mediasfu-vue

v1.1.2

Published

mediasfu-vue – Vue 3 WebRTC SDK for video conferencing, webinars, broadcasts, live streaming, chat, recording, whiteboard, and AI agents. Composition API, prebuilt rooms, and fully custom UIs.

Readme

MediaSFU Vue SDK

Build Vue 3 meetings, webinars, broadcasts, chat rooms, classrooms, live-sales experiences, podcasts, and other app-owned real-time products. MediaSFU manages signaling, WebRTC transports, room state, and media lifecycle; your application can keep the supplied UI, replace selected surfaces, or render everything.

mediasfu-vue is a Vue 3 WebRTC SDK for video conferencing, video calls, webinars, interactive live streaming, screen sharing, recording, whiteboards, polls, breakout rooms, chat, translation-aware rooms, AI-assisted experiences, prebuilt UI, component overrides, and fully headless custom UI.

npm install mediasfu-vue

Import the package stylesheet once:

import 'mediasfu-vue/dist/mediasfu-vue.css';

Choose your integration level

| Goal | Start with | | --- | --- | | Ship a complete room quickly | MediasfuGeneric, MediasfuConference, MediasfuWebinar, MediasfuBroadcast, or MediasfuChat | | Use the premium themed shell | ModernMediasfuGeneric | | Brand selected cards, controls, or modals | uiOverrides and custom card props | | Replace the complete visible workspace | customComponent | | Own rendering, state, and controls | :return-u-i="false" with useMediasfuHeadless() |

First working room

<script setup lang="ts">
import { ModernMediasfuGeneric } from 'mediasfu-vue';
</script>

<template>
  <ModernMediasfuGeneric
    :credentials="{ apiUserName: 'your-api-username', apiKey: 'your-api-key' }"
    :connect-media-s-f-u="true"
  />
</template>

Use inline credentials only for fast local or private development. For MediaSFU Open, pass local-link instead.

MediaSFU Open is your own running media server. You deploy and operate it, then point local-link at that server's reachable URL. The prop does not start a local server; localhost works only when the browser and MediaSFU Open are on the same machine.

Secure create/join proxy for production

For a public app, pass syntactically valid client placeholders and inject both room callbacks. Each callback posts only payload to your authenticated backend. The backend authorizes the user and replaces the placeholders with real MediaSFU credentials stored in private environment variables.

import type { ModernMediasfuGenericProps } from 'mediasfu-vue';

type CreateRoomOnMediaSFUType = NonNullable<
  ModernMediasfuGenericProps['createMediaSFURoom']
>;
type JoinRoomOnMediaSFUType = NonNullable<
  ModernMediasfuGenericProps['joinMediaSFURoom']
>;

export const clientPlaceholderCredentials = {
  apiUserName: 'client00',
  apiKey: '0'.repeat(64),
};

type RoomResult = Awaited<ReturnType<CreateRoomOnMediaSFUType>>;

async function proxyRoom(path: 'create' | 'join', payload: unknown): Promise<RoomResult> {
  const response = await fetch(`/api/rooms/${path}`, {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  const body = await response.json().catch(() => ({}));
  if (!response.ok || body.success === false) {
    return {
      success: false,
      data: { error: body.error ?? `Room ${path} failed (${response.status}).` },
    };
  }
  return { success: true, data: body.data };
}

export const createMediaSFURoom: CreateRoomOnMediaSFUType = ({ payload }) =>
  proxyRoom('create', payload);
export const joinMediaSFURoom: JoinRoomOnMediaSFUType = ({ payload }) =>
  proxyRoom('join', payload);
<ModernMediasfuGeneric
  :credentials="clientPlaceholderCredentials"
  :create-media-s-f-u-room="createMediaSFURoom"
  :join-media-s-f-u-room="joinMediaSFURoom"
/>

The placeholders are routing inputs, not authentication. The server must authenticate the app user, allowlist the room payload, enforce duration, capacity, and role policy, rate-limit requests, call MediaSFU with server-only credentials, and return { success, data }. Inject both callbacks so no path falls back to the default credential-bearing request.

For an embedded room, set container-width-fraction and container-height-fraction between 0 and 1. When either is below 1, the room fills its parent rather than claiming the viewport.

Customize without rebuilding the runtime

<script setup lang="ts">
import { computed } from 'vue';
import {
  MediasfuConference,
  type MediasfuUICustomOverrides,
} from 'mediasfu-vue';
import BrandedMessages from './BrandedMessages.vue';
import ProductControls from './ProductControls.vue';

const uiOverrides = computed<MediasfuUICustomOverrides>(() => ({
  messagesModal: { component: BrandedMessages },
  controlButtons: { component: ProductControls },
}));
</script>

<template>
  <MediasfuConference
    local-link="https://media.example.test"
    :ui-overrides="uiOverrides"
  />
</template>

Use customComponent for a completely different visible workspace while the room component keeps lifecycle ownership. Use the composable below when you want explicit state and action groups as well.

Reuse SDK panels in your own layout

Headless mode can combine your application layout with exported SDK controls. Keep the room engine mounted with :return-ui="false", receive its parameter publications, and pass the latest room parameters to the panel you import.

Keep modal visibility connected to the room:

  1. Open the panel through the room's matching updater, such as updateIsRecordingModalVisible(true).
  2. Bind the component's isRecordingModalVisible prop (template: :is-recording-modal-visible) to the current room value, and make its onClose callback call updateIsRecordingModalVisible(false).
  3. Pass the current room parameters and the component's required callbacks, including recording confirmation and start actions.
  4. Customize supported styles, wrappers, or overrides without replacing the underlying room callbacks.

Visibility props differ between components; use the exported component's contract, not a generic isVisible prop for every panel. Do not maintain a second independent visibility flag. With headless mode, built-in sidebar navigation is not your application's navigation.

Opening a panel does not start recording or grant media permission. Keep confirmation, permission checks, and teardown under the room engine's control.

Render the complete standard UI from the headless engine

Use ModernMediasfuGenericHead when your page needs the complete MediaSFU room UI at a different point in its layout without mounting a second room engine. The head target must appear before the engine in the template so Vue can move the engine's exact compiled UI tree into it on the first render.

<script setup lang="ts">
import {
  ModernMediasfuGeneric,
  ModernMediasfuGenericHead,
  useMediasfuHeadless,
} from 'mediasfu-vue';

const room = useMediasfuHeadless();
</script>

<template>
  <ModernMediasfuGenericHead :parameters="room.parameters.value" />

  <ModernMediasfuGeneric
    :return-u-i="false"
    render-u-i-externally
    :source-parameters="room.sourceParameters"
    :update-source-parameters="room.updateSourceParameters"
    @media-changed="room.onMediaChanged"
  />
</template>

The room engine still owns sockets, tracks, modal visibility, and sidebar navigation. For more than one room on a page, give each engine a unique external-ui-target such as #support-room, and pass the matching target-id (support-room) to its head.

Feature-rich headless quick start

This example selects the best incoming stream, renders it, keeps all prepared remote audio mounted, publishes local controls, and makes failures visible.

<script setup lang="ts">
import { computed, ref } from 'vue';
import {
  AudioGrid,
  ModernMediasfuGeneric,
  useMediasfuHeadless,
} from 'mediasfu-vue';

const room = useMediasfuHeadless();
const notice = ref('');

const primary = computed(() => {
  const share = room.screenShare.value;
  if (share.stream) return { stream: share.stream, muted: share.isLocal };
  const remote = room.remoteVideos.value[0]?.stream;
  if (remote) return { stream: remote, muted: false };
  return room.localVideo.value
    ? { stream: room.localVideo.value, muted: true }
    : null;
});

async function run(action: () => Promise<{ ok: boolean; error: string }>) {
  const result = await action();
  notice.value = result.ok ? '' : result.error;
}
</script>

<template>
  <ModernMediasfuGeneric
    local-link="https://media.example.test"
    :connect-media-s-f-u="true"
    :return-u-i="false"
    :source-parameters="room.sourceParameters"
    :update-source-parameters="room.updateSourceParameters"
    @media-changed="room.onMediaChanged"
  />

  <p>{{ room.ready.value ? 'Room ready' : room.readiness.value.reason }}</p>
  <p>{{ room.participants.value.length }} participants</p>
  <video
    v-if="primary"
    :srcObject="primary.stream"
    :muted="primary.muted"
    autoplay
    playsinline
  />

  <button :disabled="!room.ready.value" @click="run(room.controls.toggleMic)">
    {{ room.micOn.value ? 'Mute' : 'Unmute' }}
  </button>
  <button :disabled="!room.ready.value" @click="run(room.controls.toggleCamera)">
    {{ room.cameraOn.value ? 'Camera off' : 'Camera on' }}
  </button>
  <button :disabled="!room.ready.value" @click="run(room.controls.toggleScreenShare)">
    Share screen
  </button>
  <button @click="run(room.controls.leave)">Leave</button>
  <p v-if="notice" role="alert">{{ notice }}</p>

  <!-- Audio is independent of the visible video page; mount every entry. -->
  <div class="remote-audio" aria-hidden="true">
    <AudioGrid :components-to-render="room.audioComponents.value" />
  </div>
</template>

<style scoped>
.remote-audio {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
}
</style>

The composable exposes both consumption and production paths:

  • consume with localVideo, remoteVideos, screenShare, audioComponents, and participants computed refs;
  • publish or switch normal devices with controls, including microphone, camera, screen share, device selection, camera flip, chat, and leave;
  • publish app-created media with produce.media, produce.canvas, produce.element, or produce.display, then use replaceTrack or stop;
  • build host/co-host tools with moderation.permissions and the moderation actions;
  • build recording, whiteboard, poll, and breakout UI with session.

Every action returns { ok, error }. Show error to the user. Keep sourceParameters stable, accept every publication, and bind mediaChanged; do not add polling. Never call getUpdatedAllParams() from a template, computed getter, watcher, or timer because it republishes. Pure reads use getCurrentParams().

Release checklist

  • Test microphone/camera denial, no-device state, autoplay policy, device switching, network loss, rejoin, and screen-share ending.
  • Mount every prepared audio component, not only the visible video page.
  • Gate moderation and session buttons on permissions and current room state.
  • Stop app-created tracks and await Leave before unmounting the room.
  • Keep reusable Cloud credentials and privileged room operations on your server.

Documentation

Working examples

Virtual backgrounds and breakout rooms in a custom Vue UI

Keep ModernBackgroundModal wired to the newest room publication and use the room's visibility flag and updater rather than a second Vue ref. Render local camera media from useMediasfuHeadless().localVideo; it resolves the active processed/virtual stream before the raw camera, so self-view matches the stream published to everyone else.

Reuse ModernBreakoutRoomsModal with the same live room bag when you want the built-in planner. Save assignments before Start and show action errors in your page. Filtering tiles is not a breakout transition: the SDK must update room membership and pause/resume consumers for the participant's active room.

  • MediaSFU QuickStart Apps — runnable Cloud, MediaSFU Open, custom-prejoin, backend-proxy, and custom-UI examples across SDKs.
  • SpacesTek InitialFinalAdvanced — a staged path from a starter room to a product-owned Spaces-style experience.
  • MediaSFU Agents — multimodal voice/vision agent starters across supported frameworks.
  • MediaSFU VOIP — telephony, dialer, room-lifecycle, and agent/human handoff reference clients.

License

Host leave and rejoin

Hosts now see Leave room and End for everyone. The first keeps the room active and allows rejoin. Programmatic callers pass endRoomOnHostExit: false; existing calls default to true.

MIT. See LICENSE.