Browser ESM build (non-React)
The Browser ESM build is the recommended path for non-React applications. The bundle is self-contained - React, React DOM, and TanStack Query are baked in, so you do not need to load peer-dependency scripts separately. You import it dynamically as an ES module, which means no build step and no <script src> chain.
Prerequisites
| Requirement | Details |
|---|---|
| CDN URL | Environment-specific - obtain from your Commotion account manager. |
| App key | Organization-level app key - obtain from your Commotion account manager. |
| Modern browser | Any browser with dynamic ES module import() support (Chrome 63+, Firefox 67+, Safari 11.1+, Edge 79+). |
Minimal example
<div id="copilot-container"></div>
<script type="module"> const COPILOT_CDN_URL = '<YOUR_CDN_URL>'; // env-specific const APP_KEY = '<YOUR_APP_KEY>';
const { initializeCopilotSDK, CmtnCopilot } = await import(COPILOT_CDN_URL);
initializeCopilotSDK(APP_KEY, { environment: 'production' });
const instance = CmtnCopilot.init({ containerId: 'copilot-container', mode: 'embed', userId: 'user-123', });</script>Step-by-step
-
Obtain credentials.
Your Commotion account manager provides two values: the env-specific CDN URL and your app key. None of these can be derived from public sources.
-
Dynamically import the SDK.
The bundle is published as an ES module. Load it with dynamic
import():const { initializeCopilotSDK, CmtnCopilot } = await import(COPILOT_CDN_URL);No peer-dependency
<script>tags are needed - the bundle ships its own React, React DOM, and TanStack Query. -
Initialize the SDK once per page.
initializeCopilotSDK(APP_KEY, { environment: 'production' });environmentis optional. See Initialization options for valid values. -
Mount the Copilot into a container.
CmtnCopilot.init()is a static class method - it does not require an SDK instance object. Pass an options object with at minimum acontainerIdand amode.const instance = CmtnCopilot.init({containerId: 'copilot-container',mode: 'embed',}); -
Tear down when finished.
Use the instance method, or the static helpers for multi-instance pages:
instance.destroy(); // unmount this widgetCmtnCopilot.destroy('copilot-container'); // unmount by container IDCmtnCopilot.destroyAll(); // unmount every active widget
Initialization options
initializeCopilotSDK(appKey, options?) is called once per page load. The options object is optional.
| Option | Type | Default | Description |
|---|---|---|---|
environment | string | 'production' | One of 'development', 'staging', 'production'. |
CmtnCopilot.init() configuration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
containerId | string | Yes | - | DOM element ID where the widget mounts. |
mode | string | Yes | - | embed, preview, link_preview, or widget. See Modes. |
userId | string | No | auto-generated | Unique identifier for the current user. |
workerId | string | No | '' | ID of the worker the Copilot interacts with. |
agentId | string | No | '' | ID of the AI agent (requires workerId). |
type | string | No | 'worker' | 'worker' or 'agent'. |
version | number | null | No | null | Version of the worker / agent (internal use). |
shadowDom | boolean | No | true | Render inside a shadow root to isolate styles. |
See the full props reference for prop semantics shared with the React build.
Static and instance methods
const instance = CmtnCopilot.init({ /* config */ });
// Instance methodsinstance.destroy(); // unmount this widgetinstance.updateConfig({ mode: 'preview' }); // hot-update props on the existing instance
// Static methods (available without an instance)CmtnCopilot.destroy('copilot-container'); // unmount by container IDCmtnCopilot.destroyAll(); // unmount every active widgetCmtnCopilot.getActiveInstances(); // returns container IDs of active widgets| Method | Kind | Description |
|---|---|---|
instance.destroy() | instance | Unmount this widget and clean up event listeners. |
instance.updateConfig(partial) | instance | Hot-update one or more props on an existing instance. |
CmtnCopilot.destroy(containerId) | static | Unmount the widget bound to containerId. |
CmtnCopilot.destroyAll() | static | Unmount every active widget on the page. |
CmtnCopilot.getActiveInstances() | static | Return the container IDs of all currently active widgets. |
Lazy loading pattern
For pages where the Copilot only opens after a user action (for example, a floating chat button), defer the dynamic import() and initializeCopilotSDK() call until first use:
let sdkModule = null;let sdkInitialized = false;
async function loadSDK() { if (sdkModule) return sdkModule; sdkModule = await import(COPILOT_CDN_URL); return sdkModule;}
async function ensureSDKInitialized() { if (sdkInitialized) return; const { initializeCopilotSDK } = await loadSDK(); initializeCopilotSDK(APP_KEY, { environment: 'production' }); sdkInitialized = true;}
async function openCopilot() { await ensureSDKInitialized(); const { CmtnCopilot } = await loadSDK(); return CmtnCopilot.init({ containerId: 'copilot-container', mode: 'widget', });}When to use Browser ESM
| Scenario | Use Browser ESM? |
|---|---|
| Plain HTML, no build system | Yes |
| jQuery or other non-React frameworks | Yes |
| WordPress, Shopify, or CMS-rendered pages | Yes |
| Floating chat button with lazy initialization | Yes |
| Modern React 18+ application | No - use the React build |
Environment without dynamic import() support | No - use the UMD build |