ReaktlyDocs
Getting Started

Widget Installation

Add the Reaktly chat widget to any website — script tag, deferred loader, framework notes, and verification.

Prerequisites

  • A Reaktly operator with a widget configuration
  • Your operator key (op_…) — reveal it in the dashboard under the widget's Installation tab (Reveal key)
  • The domains that may embed the widget, listed under Allowed domains (see Restrict embedding)

1. Embed the widget

Paste this snippet into your site, just before the closing </body> tag:

<script type="module">
  import "https://storage.googleapis.com/reaktly-solis-cdn/latest/solis-widget.js";

  window.solis("init", {
    operatorKey: "op_live_…",
  });
</script>

The bundle is a side-effecting ES module: importing it registers the global window.solis(...) command. init fetches your widget configuration and renders the launcher.

The dashboard shows the same snippet with your operator key filled in — copy it from the widget's Installation tab.

Optional init options

OptionTypeDescription
operatorKeystringRequired. Your operator's public key (op_…)
localestringForce the widget language, e.g. "de". Omit to let the widget use the visitor's browser language
contact.userIdstringYour identifier for the visitor — lets the AI and your operators address them consistently
contact.emailstringVisitor email
contact.namestringVisitor name
showLauncherbooleanSet false when your own button should open the chat (client-owned trigger)
panelIdstringid for the widget element, so your own trigger can point at it with aria-controls

2. Verify the installation

  1. Load the page in a browser and wait for the launcher to appear in the bottom corner.
  2. Open the chat and send a message — it should be answered from your knowledge base.
  3. Watch the browser console: the widget reports a missing or rejected operator key there instead of failing silently.

The bundle is ~150 kB gzipped. The loader is the small alternative: it installs window.solis as a queue (about 1 kB) and fetches the bundle only when the first visitor actually interacts with the chat.

<script src="https://storage.googleapis.com/reaktly-solis-cdn/latest/solis-loader.umd.js"></script>
<script>
  window.solis("init", { operatorKey: "op_live_…" });
</script>

<button type="button" data-solis-toggle aria-expanded="false">Questions?</button>
  • Any element with a data-solis-* attribute becomes a trigger — no inline JavaScript, which makes this work in CMS-built headers.
  • The first click waits for the download. To warm the bundle up in the background instead, add data-idle="3000" (milliseconds) or data-preload="eager".
  • The loader state is observable on the document element: <html data-solis-loader> is idle, loading or loaded.

Pin a version in production

latest/ always serves the newest release — convenient while evaluating, but a moving target. For production, pin the bundle and verify its integrity:

<link
  rel="stylesheet"
  href="https://storage.googleapis.com/reaktly-solis-cdn/v1.26.0/solis-widget.css"
  integrity="sha384-…"
  crossorigin="anonymous"
/>
<script
  type="module"
  src="https://storage.googleapis.com/reaktly-solis-cdn/v1.26.0/solis-widget.js"
  integrity="sha384-…"
  crossorigin="anonymous"
></script>
<script>
  window.solis("init", { operatorKey: "op_live_…" });
</script>

The hashes come from …/v1.26.0/integrity.json (one sha384 per shipped file). crossorigin="anonymous" is required — without it the browser does not check the hash. A pinned version is never overwritten, so rolling back means pointing at the previous version.

Framework notes

Next.js / React

Import the bundle in a client component and initialise it once:

"use client";

import { useEffect } from "react";

export function ReaktlyWidget({ operatorKey }: { operatorKey: string }) {
  useEffect(() => {
    let active = true;
    import(
      "https://storage.googleapis.com/reaktly-solis-cdn/latest/solis-widget.js"
    ).then(() => {
      if (!active) return;
      window.solis("init", { operatorKey });
    });
    return () => {
      active = false;
      window.solis("destroy");
    };
  }, [operatorKey]);

  return null;
}

A React component package is on the roadmap; until it ships, the effect above is the supported integration.

WordPress

Add the snippet from step 1 to your theme's footer.php, or use a "insert headers and footers" plugin. A dedicated Reaktly plugin is on the roadmap.

Plain HTML

The snippet from step 1 is all you need — it works on static sites and any server-rendered stack.

Control the widget from your page

Every command is a window.solis(...) call:

CommandEffect
window.solis('open') / ('close') / ('toggle')Open, close, or toggle the chat window
window.solis('show') / ('hide')Show or hide the launcher (e.g. on checkout pages)
window.solis('update', options)Update the configuration without a reload
window.solis('destroy')Remove the widget and its listeners

Events

Subscribe with window.solis('on', event, handler) and unsubscribe with off:

EventFires when
readyThe widget finished initialising
open / closeThe chat window opened or closed
message:sent / message:receivedA message left or arrived
errorThe widget hit an error

Every event is also dispatched as a DOM event (solis:open, solis:close, …) on window, document, and the widget element — so plain HTML pages can react without the JavaScript API.

Client-owned trigger

When your own button should open the chat, switch off the launcher and drive the widget yourself:

<script type="module">
  import "https://storage.googleapis.com/reaktly-solis-cdn/latest/solis-widget.js";

  window.solis("init", {
    operatorKey: "op_live_…",
    showLauncher: false,
    panelId: "support-chat",
  });
</script>

<button type="button" onclick="window.solis('toggle')">Chat with us</button>

Focus is handled for you: the widget remembers what opened it and returns focus there on close. Set panelId if you want aria-controls on your trigger, and reflect aria-expanded from the solis:open / solis:close events.

Restrict embedding

Allowed domains in the widget settings restrict which origins may use your widget (OriginGuard). One pattern per line:

PatternMatches
example.comExact domain
*.example.comAll subdomains
https://app.example.comSpecific protocol and domain

Leaving the list empty allows every domain. Once you list domains, only matching origins are accepted.

Browser support

EngineMinimum
Chrome / Edge111
Safari16.2
Firefox113

The floor is set by color-mix() in the widget's design tokens. There is no transpiled build — below the floor the widget renders nothing rather than breaking the layout.

Troubleshooting

SymptomCheck
Nothing rendersConsole for an operator-key error; the key was revealed for the correct operator
Widget renders, but requests are rejectedThe page's origin is missing from Allowed domains
Launcher missing, chat opens via your buttonExpected when showLauncher: false
Chat opens but stays emptyThe operator's widget configuration failed to load — check the network tab for the config request

Next steps

On this page