JavaScript SDK

Embed a Meeto call with plain JavaScript

Use the zero-dependency @meeto/embed-core package when your site does not use React or Vue, or when you want direct control over the iframe widget. The same package exposes a typed MeetoWidget class, a Web Component and the browser loader behind the no-build snippet.

Install the framework-agnostic package

Install the published core package in a browser application or a static-site build. It has no runtime dependencies.

npm install @meeto/embed-core

Create a public key in the widget dashboard and allow both your local and production origins before testing. A key that does not allow the current origin cannot load the iframe.

Create an inline or floating widget

Construct MeetoWidget only after the DOM exists. An inline widget fills the element you pass as container, so give that element a real height.

import { MeetoWidget } from "@meeto/embed-core";

const call = new MeetoWidget({
  key: "mk_live_…",
  container: "#call",
  roomName: "support",
  name: "Visitor",
  lang: "en",
});

const stop = call.on("joined", (event) => {
  console.log("joined room", event.room);
});

// Later, during page teardown:
stop();
call.destroy();

roomName is a persistent shared destination such as support. Use room when your application already has a room code. Leave both unset when the widget should create and reuse a room for the current tab.

MeetoWidget options implemented by @meeto/embed-core
OptionBehavior
keyRequired public embed key beginning with mk_live_.
containerAn HTMLElement or selector for inline mode; omit it in bubble mode.
modeinline (default) or bubble, a floating button and panel.
roomName / roomPersistent named room or an existing room code.
name / lang / accentPrefill a display name, choose UI language or set a hex accent color.
position / labelBubble corner and accessible label for the floating button.
autoJoinJoin immediately only for an open-door room; the browser still requests media permission.
acceleratedUse call.meeto.me for an isolation-ready iframe; speed-up needs compatible headers on the parent page.

Subscribe to typed events and controls

Call on(type, handler) to receive one event type and keep the returned function when you need to unsubscribe. The alternative onEvent option receives every event.

call.on("phase", (event) => {
  document.body.dataset.callPhase = event.phase;
});
call.on("participants", (event) => {
  console.log("participants", event.count);
});
call.on("error", (event) => {
  console.error(event.code, event.message);
});

call.join("Visitor");
call.setMicrophone(true);
call.setCamera(false);
call.leave();
Events emitted by the core widget
EventPayload or meaning
readyThe iframe loaded and completed the handshake.
phaseprejoin, room, finished or error.
joinedThe visitor entered; includes room and identity.
leftThe visitor left; includes reason and durationSec.
participantsThe participant count changed.
errorThe widget reports a code and human-readable message.

open and close control the bubble panel and are no-ops for inline mode. frame exposes the iframe for advanced styling, while room reports the current room code after a join.

Use the <meeto-room> Web Component

The same package exports a custom element for plain HTML and stacks that support custom elements. Register it once in a browser, then give the element a height.

<meeto-room
  key="mk_live_…"
  room-name="support"
  name="Visitor"
  style="display:block;height:520px"
></meeto-room>
<script type="module">
  import { registerMeetoElement } from "@meeto/embed-core";
  registerMeetoElement();

  document.querySelector("meeto-room")
    ?.addEventListener("meeto:joined", (event) => {
      console.log("joined", event.detail.room);
    });
</script>

The element re-dispatches meeto:ready, meeto:phase, meeto:joined, meeto:left, meeto:participants and meeto:error as bubbling CustomEvents. Setting an observed attribute later rebuilds the iframe, so update identity options deliberately.

Production checklist and boundaries

Allow the exact origin

Register scheme, host and any non-default port in the embed-key settings. Preview and production domains are different origins.

Keep a visible height

The inline iframe is 100% of its container. A parent without height makes a functioning call look blank.

Clean up on unmount

Call destroy when your page, route or custom-element host is removed so the iframe and message listener do not remain.

Understand accelerated mode

accelerated: true can use multithreaded WASM only when the parent is cross-origin isolated with compatible COOP and COEP headers. Without them the widget keeps the normal single-threaded path.

Current embedded-call limits
PlanCurrent limit
FreeUp to 10 participants and 60-minute calls; cloud recording and AI summary are not included.
ProUp to 25 participants, no Free time limit within the paid concurrency allowance, recording and 10 AI-summary hours per month.
All plansPowered by Meeto branding remains visible and the public key stays origin-locked.

The widget uses WebRTC encryption in transit and is not end-to-end encrypted. Keep server credentials outside browser code; only the origin-locked public embed key belongs in this package.

Frequently asked questions

What does @meeto/embed-core contain?

It is the zero-dependency, framework-agnostic JavaScript package. MeetoWidget mounts the browser call iframe, exposes typed events and controls, and the package also exports the <meeto-room> Web Component and registration helper.

Can I construct MeetoWidget during server rendering?

Importing the package is SSR-safe, but the MeetoWidget constructor requires a browser window and document. Create it after mount, for example in useEffect, onMounted or DOMContentLoaded.

Which key belongs in browser code?

Use the public mk_live_… embed key created in the Meeto dashboard. Add the exact site origins to that key. Never put an API secret, host secret or other server credential in a browser bundle.

What happens when I destroy a widget?

destroy removes the iframe, message listener, bubble UI, presence timer and event subscriptions. Calls after destroy are safe no-ops, so call destroy when the page or component unmounts.

Put it to work

Open your Meeto account to set it up, or explore the working product first.