message.comDevelopers

Build a mobile chat widget

Until native iOS and Android SDKs ship, embed the web widget inside a WebView. Works on iOS (WKWebView), Android (WebView), and React Native (react-native-webview). Pass data-fullscreen="true" so the widget renders without the floating launcher.

Native SDKs are coming. See iOS and Android. Until they ship, the WebView pattern is the supported approach.

React Native

ChatScreen.tsx
// React Native + react-native-webview
import { WebView } from "react-native-webview";

const HTML = `<!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1" /></head>
<body style="margin:0">
<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;
    j.dataset.fullscreen='true';
    d.head.appendChild(j);
  })(document,'script','YOUR_WORKSPACE_ID');
</script>
</body></html>`;

export default function ChatScreen() {
  return (
    <WebView
      originWhitelist={["*"]}
      source={{ html: HTML, baseUrl: "https://yourapp.local" }}
      javaScriptEnabled
      domStorageEnabled
    />
  );
}

iOS (Swift / WKWebView)

ChatViewController.swift
// iOS / Swift / WKWebView
import WebKit

let html = """
<!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1" /></head>
<body style="margin:0">
<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;
    j.dataset.fullscreen='true';
    d.head.appendChild(j);
  })(document,'script','YOUR_WORKSPACE_ID');
</script>
</body></html>
"""

let webView = WKWebView(frame: .zero)
webView.loadHTMLString(html, baseURL: URL(string: "https://yourapp.local"))

Android (Kotlin / WebView)

ChatActivity.kt
val html = """
  <!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1" /></head>
  <body style="margin:0">
  <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;
      j.dataset.fullscreen='true';
      d.head.appendChild(j);
    })(document,'script','YOUR_WORKSPACE_ID');
  </script>
  </body></html>
""".trimIndent()

val webView = findViewById<WebView>(R.id.webview)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.loadDataWithBaseURL("https://yourapp.local", html, "text/html", "UTF-8", null)

Identify the signed-in user

Bridge native to JS. iOS uses evaluateJavaScript, Android uses WebView.evaluateJavascript, React Native uses the injectJavaScript prop. Call Message.identify once the bridge is ready.

bridge.js
function identifyUser(user) {
  if (window.Message) callIt();
  else window.MessageOnReady = callIt;
  function callIt() {
    window.Message.identify({
      email: user.email,
      name: user.name,
      userId: user.id,
      userHash: user.userHash // computed on your server
    });
  }
}

Push notifications

Two approaches:

  • Server-side webhook + APNs / FCM. Subscribe to message:received on the agent namespace from a small relay service, look up the visitor's device token, push.
  • Polling fallback. When the app opens, call GET /api/v1/conversations?visitorId=X for unread.

Common pitfalls

  • Cookies in WebView. The widget uses cookies to keep the visitor session stable. Enable WKWebViewConfiguration.websiteDataStore on iOS and CookieManager.setAcceptCookie(true) on Android.
  • App Store review. If you collect identifiable info inside chat, list message.com as a data sub-processor in your privacy disclosures.
  • Background. When the app backgrounds, the WebView pauses. Messages received in the background should be delivered via push, not via the WebView itself.
  • RN bundler. Some Metro configurations strip data attributes. Inject the snippet via injectJavaScript if source.html does not work.

Next steps