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.
<!-- 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.
// 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.
| Platform | Method | Status |
|---|---|---|
| Cloudflare | OAuth to provision a Worker that injects the snippet | Planned |
| WordPress | Plugin from WordPress.org | Coming soon |
| Shopify | App from Shopify App Store (Built for Shopify) | Coming soon |
| Wix | App from Wix App Market | Coming soon |
| Squarespace | Code 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.
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
- Reload your page. A launcher should appear in the bottom-right corner within two seconds.
- Click the launcher. The chat panel should slide up.
- Send a test message.
- Open
app.message.comin 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.comfrom 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 newpageUrlafter a route change to push context. - iframe sandboxes. The widget cannot mount inside a fully sandboxed iframe without
allow-scriptsandallow-same-origin.