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-coreCreate 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.
| Option | Behavior |
|---|---|
| key | Required public embed key beginning with mk_live_. |
| container | An HTMLElement or selector for inline mode; omit it in bubble mode. |
| mode | inline (default) or bubble, a floating button and panel. |
| roomName / room | Persistent named room or an existing room code. |
| name / lang / accent | Prefill a display name, choose UI language or set a hex accent color. |
| position / label | Bubble corner and accessible label for the floating button. |
| autoJoin | Join immediately only for an open-door room; the browser still requests media permission. |
| accelerated | Use 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();| Event | Payload or meaning |
|---|---|
| ready | The iframe loaded and completed the handshake. |
| phase | prejoin, room, finished or error. |
| joined | The visitor entered; includes room and identity. |
| left | The visitor left; includes reason and durationSec. |
| participants | The participant count changed. |
| error | The 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.
| Plan | Current limit |
|---|---|
| Free | Up to 10 participants and 60-minute calls; cloud recording and AI summary are not included. |
| Pro | Up to 25 participants, no Free time limit within the paid concurrency allowance, recording and 10 AI-summary hours per month. |
| All plans | Powered 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.