Install and open the product demo
Use a publishable surface key from the Demoloop workspace. Never place private server credentials in browser context.
npm install demoloop-widget
import {
init, identify, setContext, track, open
} from "demoloop-widget";
await init({ key: "dl_xxx" });
identify({ id: user.id, email: user.email, company: account.name });
setContext({ section: "Billing", software: { tenant: account.name } });
track("viewed_feature", { feature: "usage-limits" });
open();Stable v1 browser API
init(options)Validate the widget key and allowed domain, load surface configuration, and mount the launcher.
identify(user)Attach the signed-in user and account traits to the product demo context.
setContext(context)Describe the current page, tenant, use case, metadata, screens, and product flows.
track(name, properties)Send recent product activity that helps the demo understand visitor intent.
registerAction(name, handler)Expose one explicit host capability. Handlers receive an AbortSignal and return structured results.
installExplorationBridge(options)Allow configured semantic focus targets and page scrolling inside a cooperative product embed.
setModules(modules)Advertise independently launchable product-demo or escalation experiences.
open(options)Open the default experience or a selected module in a modal or popup.
on(event, listener)Subscribe to ready, open, close, context, action, error, and other lifecycle events.
close() / destroy()Close the experience or remove the SDK completely during an SPA lifecycle change.
Keep application control explicit
Actions are named capabilities implemented by the host. Requests time out, can be cancelled, and cannot navigate string routes away from the host origin.
import { registerAction } from "demoloop-widget";
registerAction("open-report", async (request, { signal }) => {
const reportId = String(request.payload?.reportId || "");
if (!reportId) return false;
await openReport(reportId, { signal });
return { reportId };
});Allow semantic focus and scrolling
The bridge accepts only configured target IDs and scrolling commands from the trusted Demoloop shell. It never grants arbitrary selector or mutation access.
import { installExplorationBridge } from "demoloop-widget";
installExplorationBridge({ shellOrigin: "https://getdemoloop.com" });
// Mark safe content in your embedded product:
// <section data-demo-target="usage-chart">...</section>Start with the demo, escalate when useful
A module is an independently launchable experience. Product demos remain the primary journey; a video call can be offered when the visitor needs a human expert.
import { setModules, open } from "demoloop-widget";
setModules([
{ id: "product-demo", experience: "demo", presentation: "modal" },
{ id: "video-call", experience: "video-call", presentation: "modal" }
]);
open({ module: "product-demo" });Provider and hooks
import { DemoloopProvider, useDemoloop } from "demoloop-widget/react";
function App() {
return (
<DemoloopProvider options={{ key: "dl_xxx" }}>
<DemoButton />
</DemoloopProvider>
);
}
function DemoButton() {
const demoloop = useDemoloop();
return <button onClick={() => demoloop.open()}>See it live</button>;
}Framework neutral
Use the base package with any browser application or the hosted script loader.
Versioned contract
Cross-window context, actions, cancellations, and results use protocol version 1.0.
Bounded data
Exact origins, bounded context, domain validation, and server-side sanitization protect the bridge.
Browser-native expert handoff
Camera and microphone require explicit browser permission.
Audio and video are peer-to-peer and are not recorded by Demoloop.
Room status and signaling metadata expire independently from demo transcripts.
Restrictive enterprise networks may require a managed TURN relay for universal connectivity.
Configure a product-demo surface
Each surface has its own publishable key, allowed domains, modules, and visitor activity while remaining manageable by the same organization team.
Start free