message.comDevelopers

Widget installation Live

Drop one script tag on any HTML page. The widget bundle is roughly 50KB gzipped, loads asynchronously, and renders inside a Shadow DOM root so your site styles cannot collide with the launcher or the chat panel.

Standard install

The canonical install is a single async snippet. Paste it before the closing </body> tag (or anywhere in the document body if your server-side template forces it elsewhere). The snippet runs once on page load, requests the bundle, and mounts the launcher in the bottom-right corner.

HTML
<!-- Paste before </body> -->
<script>
  (function(d,s,o){
    var j=d.createElement(s);j.async=1;j.src='https://app.message.com/widget.js';
    j.dataset.workspace=o;
    d.head.appendChild(j);
  })(document,'script','YOUR_WORKSPACE_ID');
</script>

Replace YOUR_WORKSPACE_ID with the value from Settings → Chats → Installation in the dashboard. The workspace ID is a public identifier (safe to ship in HTML); the secret half of the workspace lives on the server.

The widget bundle is served from app.message.com/widget.js behind Cloudflare. We cache it at the edge with a long max-age and rely on the URL hash to bust stale caches when we ship updates. You do not need to update the snippet to get new features.

Next.js install

For Next.js 13+ App Router, use the next/script component with strategy="afterInteractive" so the bundle does not block hydration. Pasting raw <script> tags into JSX breaks because React strips inline scripts.

Next.js · app/layout.tsx
// app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <Script id="message-widget" strategy="afterInteractive">
          {`(function(d,s,o){
            var j=d.createElement(s);j.async=1;j.src='https://app.message.com/widget.js';
            j.dataset.workspace=o;
            d.head.appendChild(j);
          })(document,'script','YOUR_WORKSPACE_ID');`}
        </Script>
      </body>
    </html>
  );
}

For more framework-specific guides see Install on Next.js, Install on React, and Install on Vue.

One-click installs

Skip the snippet on supported platforms. See One-click installs for the full list with platform-specific install instructions.

PlatformMethodStatus
CloudflareOAuth to provision a Worker that injects the snippetPlanned
WordPressPlugin from WordPress.orgComing soon
ShopifyApp from Shopify App Store (Built for Shopify)Coming soon
WixApp from Wix App MarketComing soon
SquarespaceCode Injection (Business plan)Coming soon

Content Security Policy

If your site sends a strict Content-Security-Policy header, allowlist app.message.com on the script-src, connect-src (for the Socket.io upgrade), and img-src directives. The widget renders styles inside Shadow DOM, but the host page must allow inline style attributes to be applied to the host element.

HTTP header
Content-Security-Policy:
  script-src 'self' https://app.message.com;
  connect-src 'self' https://app.message.com wss://app.message.com;
  img-src 'self' data: https://app.message.com;
  style-src 'self' 'unsafe-inline';

Read the MDN CSP reference if you need a deeper dive on directive scoping.

Verify the install

  1. Reload your page. A launcher should appear in the bottom-right corner within two seconds.
  2. Click the launcher. The chat panel should slide up.
  3. Send a test message.
  4. Open app.message.com in another tab. A new conversation appears in the inbox.

If nothing appears, open the browser console. The bundle logs a single startup line tagged [message] with the workspace ID it picked up. A missing log means the script tag never ran (typo or CSP blocked it).

Common pitfalls

  • Multiple snippets on one page. If two copies of the snippet load, the second is ignored. Audit your tag manager and theme partials.
  • Caching plugins rewriting the snippet. Some WordPress optimisation plugins try to defer or combine the snippet, which breaks the async preamble. Exclude app.message.com from script optimisation.
  • SPA route changes. The bundle is route-agnostic and survives client-side navigation. You do not need to re-inject it. Call Message.update(...) with the new pageUrl after a route change to push context.
  • iframe sandboxes. The widget cannot mount inside a fully sandboxed iframe without allow-scripts and allow-same-origin.