diff --git a/docs.json b/docs.json index 079060fc..52a3a09a 100644 --- a/docs.json +++ b/docs.json @@ -426,7 +426,8 @@ "pages": [ "/webchat/integrations/wordpress", "/webchat/integrations/wix", - "/webchat/integrations/webflow" + "/webchat/integrations/webflow", + "/webchat/integrations/react-native" ] }, { diff --git a/webchat/integrations/react-native.mdx b/webchat/integrations/react-native.mdx new file mode 100644 index 00000000..3bf1ca99 --- /dev/null +++ b/webchat/integrations/react-native.mdx @@ -0,0 +1,384 @@ +--- +title: React Native +description: Add your bot in any React Native or Expo app using react-native-webview. +--- + + +**Why a WebView?** Botpress Cloud Webchat is a **browser** client (`window.botpress`). React Native does not run that API natively, so the supported pattern is to run the official Webchat **inside a WebView** and communicate with `postMessage` and `injectJavaScript`. + +The [`@botpress/webchat`](/webchat/react-library/get-started) package targets web React. For native apps, use the WebView approach below. + + +## Prerequisites + + +You will need: + +* A [published bot](/get-started/quick-start) +* A React Native or Expo project +* [`react-native-webview`](https://github.com/react-native-webview/react-native-webview) + + +## 1. Copy your Webchat embed code from the dashboard + +Before you write any app code, grab the same embed snippet you would use on a website. In the Botpress dashboard: + +1. Open your bot's Workspace and select the bot you want to embed. +2. In the left sidebar, go to **Webchat** > **Deploy Settings**. +3. Copy the **Embed code**: + + + Embed code from Dashboard + Embed code from Dashboard + + +## 2. Lay out your project files + +Create a **`src`** folder at your project root if you do not already have one. A typical layout: + +| Path | Purpose | +|------|---------| +| **`src/botpress/getBotpressWebchat.js`** | Builds `{ html, baseUrl }` for the WebView. | +| **`src/botpress/BpWidget.js`** | WebView component and event bridge (`onMessage`); blocks marketing site navigation on close. | +| **`src/botpress/BpWidget.d.ts`** | *(Optional, TypeScript only)* Types for `BpWidget`. | +| **`src/config/botpressConfig.js`** | Webchat URLs and optional `botId`. **Step 4** shows what goes inside `export const botpressConfig = { ... }`. | + +You can instead keep files flat under `src/` and use relative imports. The examples below assume **`src/botpress/`** and **`src/config/`**. + +**Path alias (optional, Expo / TypeScript):** In **`tsconfig.json`**, merge into `compilerOptions`: + +```json +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } +} +``` + +Then imports like `import BpWidget from '@/botpress/BpWidget'` resolve. If you skip aliases, use relative imports only. + +## 3. Install react-native-webview + +From your project root: + + + +```bash Expo +npx expo install react-native-webview +``` + +```bash npm +npm install react-native-webview +``` + +```bash yarn +yarn add react-native-webview +``` + +```bash pnpm +pnpm add react-native-webview +``` + + + + + For bare React Native, follow [react-native-webview linking](https://github.com/react-native-webview/react-native-webview/blob/master/docs/Getting-Started.md) if your setup requires it. Expo users can also see the [Expo WebView docs](https://docs.expo.dev/versions/latest/sdk/webview/). + + +## 4. Save your Studio script URLs in one config file + +Create **`src/config/botpressConfig.js`** and export a single **`botpressConfig`** object. Paste the script URLs from **Studio → Webchat → Embed** (the same values from your embed snippet). The HTML helper and the widget only read **`botConfig`**: you pass it in, and you do not duplicate URLs inside `getBotpressWebchat.js` or `BpWidget.js`. + + +**Sensitive values:** `botId` and embed URLs identify your bot. For a public repository, do not commit real values, use placeholders in git or keep this file out of version control. + + + +```javascript +export const botpressConfig = { + botId: "YOUR_BOT_ID", + injectScriptUrl: "https://cdn.botpress.cloud/webchat/v3.6/inject.js", + embedScriptUrl: "https://files.bpcontent.cloud/YOUR/PATH/YOUR-GENERATED.js", + baseUrl: "https://cdn.botpress.cloud/", +}; +``` + + +## 5. Build the HTML page that loads Webchat + +Add **`src/botpress/getBotpressWebchat.js`**. It builds a tiny HTML page (inject script, embed script, full height container) and returns **`{ html, baseUrl }`** so you can pass it straight into ``. + +```javascript getBotpressWebchat.js +/** + * Builds the HTML page loaded by the WebView for Botpress Cloud webchat v3. + * @param {Record} botConfig Pass `botpressConfig` from src/config/botpressConfig.js + * @returns {{ html: string, baseUrl: string }} + */ +const getBotpressWebchat = (botConfig) => { + // Official inject script (Botpress loader); falls back to a known default if omitted. + const injectUrl = + botConfig.injectScriptUrl || + "https://cdn.botpress.cloud/webchat/v3.6/inject.js"; + // Studio-generated bundle URL (required). This wires your bot into the page. + const embedUrl = botConfig.embedScriptUrl; + if (!embedUrl || typeof embedUrl !== "string") { + throw new Error( + "botConfig.embedScriptUrl is required (paste the second script URL from Studio embed)", + ); + } + // Origin passed to WebView as `baseUrl` so relative URLs in the HTML resolve correctly. + const baseUrl = botConfig.baseUrl || "https://cdn.botpress.cloud/"; + + // Minimal HTML shell: viewport, full-height container, then inject + embed scripts (matches Studio order). + const html = ` + + + + + + Chatbot + + + + + +`; + + // WebView expects this pair: `source={{ baseUrl, html }}`. + return { baseUrl, html }; +}; + +module.exports = getBotpressWebchat; +``` + +## 6. Show Webchat in a WebView and forward events to React Native + +Create **`src/botpress/BpWidget.js`**. It renders a **`WebView`** that loads your HTML, then relays what happens inside the page to your React Native layer: + +* Puts the page from **`getBotpressWebchat(botConfig)`** into the **`WebView`**. +* Injects a script that subscribes to **`window.botpress`** and sends each event to React Native with **`postMessage`** (you read them in **`onMessage`** as JSON strings). +* Intercepts navigation to **`botpress.com`** or **`*.botpress.com`** when the user closes the widget and runs **`window.botpress.close()`** instead to close the webchat interface. + +```javascript BpWidget.js expandable +/** + * WebView wrapper for Botpress webchat v3: window.botpress API, events to React Native via postMessage. + */ +import { WebView } from "react-native-webview"; +import getBotpressWebchat from "./getBotpressWebchat"; +import React, { useCallback, useRef } from "react"; + +const broadcastToReactNative = ` +(function () { + // Sends one event payload to React Native as a JSON string (read in onMessage). + function post(ev, data) { + try { + window.ReactNativeWebView.postMessage(JSON.stringify({ event: ev, data: data })); + } catch (e) {} + } + // Subscribes to window.botpress once it exists and mirrors events into React Native. + function wire() { + if (!window.botpress || typeof window.botpress.on !== "function") return false; + window.botpress.on("webchat:initialized", function () { + try { window.botpress.open(); } catch (e) {} + }); + [ + "message", + "webchat:initialized", + "webchat:ready", + "webchat:opened", + "webchat:closed", + "customEvent", + "error", + "conversation", + ].forEach(function (ev) { + window.botpress.on(ev, function (data) { + post(ev, data); + }); + }); + // Fallback open in case the widget did not auto-open after init. + setTimeout(function () { + try { window.botpress.open(); } catch (e) {} + }, 400); + return true; + } + // Poll until botpress is ready (or give up after ~12s) so inject order does not race. + var n = 0; + var id = setInterval(function () { + if (wire() || ++n > 240) clearInterval(id); + }, 50); +})(); +true; +`; + +const closeChatJs = ` +try { + // Programmatic close when we block a navigation attempt (through onShouldStartLoadWithRequest). + if (window.botpress && typeof window.botpress.close === "function") { + window.botpress.close(); + } +} catch (e) {} +true; +`; + +// Returns true when a navigation URL is the public Botpress marketing site (close button behavior). +function isBotpressMarketingSiteUrl(url) { + if (!url || url.startsWith("about:") || url.startsWith("blob:") || url.startsWith("data:")) { + return false; + } + try { + const h = new URL(url).hostname; + return h === "botpress.com" || h.endsWith(".botpress.com"); + } catch { + return false; + } +} + +export default function BpWidget(props) { + const { botConfig, onMessage } = props; + const webref = useRef(null); + + // Build the in-memory HTML page and origin for this WebView instance. + const { html, baseUrl } = getBotpressWebchat(botConfig); + + // Intercept navigations: block opening botpress.com inside the frame and close chat instead. + const onShouldStartLoadWithRequest = useCallback((request) => { + const { url } = request; + if (isBotpressMarketingSiteUrl(url)) { + webref.current?.injectJavaScript(closeChatJs); + return false; + } + return true; + }, []); + + // Webchat needs JavaScript and DOM storage; originWhitelist allows loading scripts from Botpress CDNs. + return ( + + ); +} +``` + + + **`onMessage` format:** `event.nativeEvent.data` is a **string**. Parse with `JSON.parse`. Shape: **`{ event: string, data: unknown }`**. + + +## 7. Optional: add TypeScript types for the widget + +If you use TypeScript, add **`src/botpress/BpWidget.d.ts`** next to **`BpWidget.js`** so imports and props are typed: + +```typescript +import type { FunctionComponent } from "react"; + +/** Props for the WebView wrapper: Studio-derived config and optional React Native message handler. */ +export interface BpWidgetProps { + botConfig: Record; + onMessage?: (e: { nativeEvent: { data: string } }) => void; +} + +/** Default export from BpWidget.js (JavaScript component with these props). */ +declare const BpWidget: FunctionComponent; + +export default BpWidget; +``` + +## 8. Render the Webchat widget on a screen + +Import **`BpWidget`** and **`botpressConfig`**, then render it full screen or inside any `View` that uses `flex: 1`. Adjust import paths to your real paths (for example Expo Router **`app/index.tsx`**, a root **`App.js`**, or **`src/screens/…`**). + +```tsx ChatScreen.tsx +import { StyleSheet, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +import BpWidget from "@/botpress/BpWidget"; +import { botpressConfig } from "@/config/botpressConfig"; + +/** Screen that fills the safe area with the Botpress WebView-powered widget. */ +export default function ChatScreen() { + return ( + + + { + // Bridge: WebView posts JSON strings; parse to `{ event, data }` for logging or app logic. + const raw = e.nativeEvent?.data; + if (!raw) return; + try { + const msg = JSON.parse(raw); + console.log(msg.event, msg.data); + } catch { + /* non-JSON */ + } + }} + /> + + + ); +} + +const styles = StyleSheet.create({ + safeArea: { flex: 1 }, + main: { flex: 1 }, +}); +``` + +## 9. Run your app + +```bash +npm install +npx expo start +``` + + +Open the iOS Simulator, Android emulator, or a physical device from there. Your bot should load inside the WebView when you run the app. + + + +## Troubleshooting + +| Problem | What to check | +|--------|----------------| +| Blank WebView | **`embedScriptUrl`** in **`botpressConfig.js`** must be the **`files.bpcontent.cloud/...`** URL from Studio. | +| Error: `embedScriptUrl is required` | Set `embedScriptUrl` in `botpressConfig`. | +| Close (X) opens the Botpress.com website | Keep **`onShouldStartLoadWithRequest`** and **`closeChatJs`** in `BpWidget.js` as shown in that section. | +| Fonts look wrong | Tune appearance in **Botpress Studio**; avoid forcing global `font-family` in the HTML shell. | +| Android keyboard covers the composer | In **`app.json`** (Expo), under **`expo.android`**, set `"softwareKeyboardLayoutMode": "resize"`. Rebuild the native Android app after changing this (not only a Metro refresh). | + +## Next steps + +Now that Webchat runs in your app, try [styling](/webchat/get-started/configure-your-webchat) it in Studio to match your product. For more control over the embed, see [Embed Webchat](/webchat/get-started/embedding-webchat) and the [Webchat interaction reference](/webchat/interact/reference).