Loader script
How the widget-mojar-loader.js script boots, what data attributes it reads, and the JS API it exposes.
The loader script (widget-mojar-loader.js) is a self-contained IIFE that initialises the Mojar chat widget. This page covers its internal boot sequence, every data-* attribute it reads, the JavaScript API it exposes on window, and the postMessage events it handles.
For the quickest path to adding the widget, start at install the SDK. For the full attribute reference (visual/UX options) see embed widget — configuration.
Boot sequence
When the browser parses the <script> tag, the loader runs this sequence:
Guard against double-initialisation. If window.mojarAiWidgetLoaded is already true, the script exits immediately. This protects against the tag being included more than once.
Read configuration from data-* attributes on the <script> element via document.currentScript.dataset. If data-agentid or data-domainuuid is missing, the script exits silently.
Expose the JS API (window.MojarAIWidget / window.MojarAIWidgetV2) immediately so calling code can call open() before the network requests complete.
Set up tab-visibility handling. A visibilitychange listener silently refreshes the session token when the user returns to the tab and the token is within 2 minutes of its 25-minute expiry.
Validate the agent and domain by posting to /api/embed/initiate-session on the Mojar host. On success, a short-lived session token is returned.
Inject animation styles (<style id="mojar-widget-animations">) and issue an iframe <link rel="preload"> for the embed chat page so the first open is faster.
Create the toggle button (unless data-hide-button="true"). If data-modal-open-by-default="true", the iframe is loaded immediately instead of waiting for the first click.
The iframe itself is created lazily — only when the user clicks the toggle button for the first time (or when data-modal-open-by-default="true").
Data attributes
The following attributes are read from the <script> tag. Required attributes must be present or the widget will not initialise.
Required
| Attribute | Description |
|---|---|
data-agentid | UUID of the Mojar agent to embed. |
data-domainuuid | UUID of the registered embed domain for the calling origin. |
Button
| Attribute | Default | Description |
|---|---|---|
data-button-position | bottom-right | Where the toggle button appears. One of bottom-right, bottom-left, top-right, top-left. |
data-button-theme | primary | Visual preset: primary (dark navy), dark, or light. Overridden by custom color attributes. |
data-button-text | Ask AI | Label rendered below the icon. |
data-hide-button | false | Set to "true" to suppress the toggle button entirely. Use the JS API to open/close programmatically. |
Colors
| Attribute | Default | Description |
|---|---|---|
data-background | #CCFF33 | Primary background color passed to the iframe. |
data-accent | #FF2C63 | Accent color passed to the iframe (used for the button icon). |
data-text-color | #11113A | Primary text color. |
data-accent-text-color | — | Text color on accented elements. |
data-card-color | #FFFFFF | Card/surface background color. |
data-info-text-color | — | Muted/informational text color. |
data-close-button-color | — | Close button icon color. |
data-close-button-hover-color | — | Close button icon color on hover. |
Other options
| Attribute | Default | Description |
|---|---|---|
data-modal-open-by-default | false | Open the widget immediately on page load. |
data-user-name | — | Optional display name passed into the chat session. |
data-project-logo | — | URL of a logo to display inside the chat iframe. |
data-contact-email | — | Contact email surfaced inside the chat for support fallback. |
data-mobile-fullscreen | false | Force fullscreen layout on all viewports (intended for testing only — production automatically applies fullscreen on viewports ≤ 768 px). |
Iframe layout
On desktop (viewport > 768 px), the iframe renders as a centered modal: width: 90%, max-width: 900px, height: 80vh, with 32 px border-radius.
On mobile (viewport ≤ 768 px), the iframe fills the viewport: position: fixed; inset: 0; width: 100vw; height: 100dvh. Only the messages list scrolls; the input is fixed at the bottom.
The loader listens to resize and orientationchange events and re-applies these styles dynamically.
JavaScript API
After successful initialisation, the loader exposes the widget API on two global names (both point to the same object):
window.MojarAIWidgetwindow.MojarAIWidgetV2
window.MojarAIWidget.open() // Show the widget (loads iframe on first call)
window.MojarAIWidget.close() // Hide the widget with a slide-down animation
window.MojarAIWidget.toggle() // Open if closed, close if open
window.MojarAIWidget.destroy() // Fully tear down the widget and release all listeners
window.MojarAIWidget.reinitialize()// Re-validate credentials and recreate the toggle buttonopen(), close(), and toggle() are no-ops while an animation is in progress (debounced at ~250–300 ms) to prevent visual jank from rapid successive calls.
Example — open the widget from your own button:
<button onclick="window.MojarAIWidget.open()">Talk to us</button>
<script
src="https://app.mojar.ai/widget-mojar-loader.js"
data-agentid="YOUR_AGENT_UUID"
data-domainuuid="YOUR_DOMAIN_UUID"
data-hide-button="true"
></script>postMessage events
The loader listens for message events from the iframe. Events from origins other than the Mojar host are ignored.
type | action | What the loader does |
|---|---|---|
MOJAR_WIDGET_COMMAND | close_widget | Calls MojarAIWidget.close(). |
MOJAR_WIDGET_COMMAND | reinitializeSession | Calls initializeValidationAndCreateButton() — re-validates the agent/domain and recreates the button. |
MOJAR_WIDGET_COMMAND | REQUEST_NEW_SESSION_TOKEN | Triggers an immediate silent token refresh and posts the new token back to the iframe as { type: "NEW_SESSION_TOKEN", token }. |
Session token lifecycle
The session token has a 25-minute lifetime (TOKEN_EXPIRY_MS = 25 * 60 * 1000). The loader schedules a setTimeout to silently call POST /api/embed/generate-token (with the agent and domain UUIDs) before expiry, then posts the refreshed token into the iframe via postMessage. On tab re-focus, if more than 23 minutes have passed since the last refresh, a refresh is triggered immediately.
Error display
If the initial session call fails, the loader renders a small error banner near the toggle button (positioned to match data-button-position) that auto-dismisses after 8 seconds. The toggle button enters an error state (red background, disabled pointer events) and reverts to normal if the widget is successfully reinitialized.