From 85a12cb0fa7a53c5f877c43f27090eef7f48d7a6 Mon Sep 17 00:00:00 2001 From: Chloe Quijano Date: Mon, 30 Mar 2026 16:06:22 -0400 Subject: [PATCH 1/2] add react native guide --- docs.json | 3 +- webchat/integrations/react-native.mdx | 375 ++++++++++++++++++++++++++ 2 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 webchat/integrations/react-native.mdx 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..5cb9afb3 --- /dev/null +++ b/webchat/integrations/react-native.mdx @@ -0,0 +1,375 @@ +--- +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` / `injectJavaScript`. + +The [`@botpress/webchat`](/webchat/react-library/get-started) package targets web React. For native apps, use the WebView approach below. + + + +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) + + + + + To embed a bot on your mobile app, you need the bot's Webchat embed code. You can get the embed code from the 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 + + + + + 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`** | Your **`inject.js`** and embed script URLs from Studio. | + + 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. + + + + 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/). + + + + + Add **`src/config/botpressConfig.js`** with the **exact URLs** from your Studio embed snippet. Replace the placeholders with your values. + + ```javascript + /** + * Paste both script URLs from Botpress Studio → Webchat → Embed. + */ + export const botpressConfig = { + /** Your bot id for your own logs / analytics */ + botId: "YOUR-BOT-UUID", + + /** First script: cdn.botpress.cloud/.../inject.js — version must match Studio */ + injectScriptUrl: "https://cdn.botpress.cloud/webchat/v3.6/inject.js", + + /** Second script: files.bpcontent.cloud/.../....js (defer in HTML) */ + embedScriptUrl: "https://files.bpcontent.cloud/YOUR/PATH/YOUR-GENERATED.js", + + /** + * HTTPS origin used as WebView `baseUrl` (resolves relative URLs inside the page). + */ + baseUrl: "https://cdn.botpress.cloud/", + }; + ``` + + + + + Next, add **`src/botpress/getBotpressWebchat.js`**. This helper hands the WebView an **`{ html, baseUrl }`** object you can pass straight into ``. + + ```javascript getBotpressWebchat.js + /** + * Builds the HTML page loaded by the WebView for Botpress Cloud webchat v3. + * @param {Record} botConfig - Use `botpressConfig` from src/config/botpressConfig.js + * @returns {{ html: string, baseUrl: string }} + */ + const getBotpressWebchat = (botConfig) => { + const injectUrl = + botConfig.injectScriptUrl || + "https://cdn.botpress.cloud/webchat/v3.6/inject.js"; + const embedUrl = botConfig.embedScriptUrl; + if (!embedUrl || typeof embedUrl !== "string") { + throw new Error( + "botConfig.embedScriptUrl is required (paste the second script URL from Studio embed)", + ); + } + const baseUrl = botConfig.baseUrl || "https://cdn.botpress.cloud/"; + + const html = ` + + + + + + Chatbot + + + + + + `; + + return { baseUrl, html }; + }; + + module.exports = getBotpressWebchat; + ``` + + + + Add **`src/botpress/BpWidget.js`**. It: + + * Loads the page from **`getBotpressWebchat(botConfig)`**. + * Injects JavaScript to subscribe to **`window.botpress`** and forward events to React Native via **`postMessage`** (`onMessage` receives JSON strings). + * Blocks top-level navigation to **`botpress.com` / `*.botpress.com`** so tapping **close** does not replace the WebView with the marketing site; it injects **`window.botpress.close()`** instead. + + ```javascript BpWidget.js expandable + /** + * WebView wrapper for Botpress webchat v3: window.botpress API, events to RN via postMessage. + */ + import { WebView } from "react-native-webview"; + import getBotpressWebchat from "./getBotpressWebchat"; + import React, { useCallback, useRef } from "react"; + + const broadcastToRn = ` +(function () { + function post(ev, data) { + try { + window.ReactNativeWebView.postMessage(JSON.stringify({ event: ev, data: data })); + } catch (e) {} + } + 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); + }); + }); + setTimeout(function () { + try { window.botpress.open(); } catch (e) {} + }, 400); + return true; + } + var n = 0; + var id = setInterval(function () { + if (wire() || ++n > 240) clearInterval(id); + }, 50); +})(); +true; +`; + + const closeChatJs = ` +try { + if (window.botpress && typeof window.botpress.close === "function") { + window.botpress.close(); + } +} catch (e) {} +true; +`; + + 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); + + const { html, baseUrl } = getBotpressWebchat(botConfig); + + const onShouldStartLoadWithRequest = useCallback((request) => { + const { url } = request; + if (isBotpressMarketingSiteUrl(url)) { + webref.current?.injectJavaScript(closeChatJs); + return false; + } + return true; + }, []); + + return ( + + ); + } + ``` + + + **`onMessage` format:** `event.nativeEvent.data` is a **string**. Parse with `JSON.parse`. Shape: **`{ event: string, data: unknown }`**. + + + + + If you use TypeScript, add **`src/botpress/BpWidget.d.ts`** next to **`BpWidget.js`**: + + ```typescript + import type { FC } from "react"; + + export interface BpWidgetProps { + botConfig: Record; + onMessage?: (e: { nativeEvent: { data: string } }) => void; + } + + declare const BpWidget: FC; + + export default BpWidget; + ``` + + + + Import **`BpWidget`** and **`botpressConfig`**, then render full-screen or inside a `View` with `flex: 1`. Adjust import paths to match your layout (Expo Router `app/index.tsx`, classic `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"; + + export default function ChatScreen() { + return ( + + + { + 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 }, + }); + ``` + + + + ```bash + npm install + npx expo start + ``` + + Then open the iOS Simulator, Android emulator, or a device. + + + + +Your bot should load inside the WebView when you run the app. + + + +## Troubleshooting + +| Problem | What to check | +|--------|----------------| +| Blank WebView | `embedScriptUrl` and `injectScriptUrl` match Studio; device online; `javaScriptEnabled` and `domStorageEnabled` are true. | +| Error: `embedScriptUrl is required` | Set `embedScriptUrl` in `botpressConfig`. | +| Close (X) opens the marketing website | Keep **`onShouldStartLoadWithRequest`** + **`closeChatJs`** in `BpWidget.js` as in the steps above. | +| Fonts / emoji 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). From 6790f122d13dee1d05858cb4b9dcdc266dda539d Mon Sep 17 00:00:00 2001 From: Chloe Quijano Date: Tue, 31 Mar 2026 15:49:28 -0400 Subject: [PATCH 2/2] update wording --- webchat/integrations/react-native.mdx | 581 +++++++++++++------------- 1 file changed, 295 insertions(+), 286 deletions(-) diff --git a/webchat/integrations/react-native.mdx b/webchat/integrations/react-native.mdx index 5cb9afb3..3bf1ca99 100644 --- a/webchat/integrations/react-native.mdx +++ b/webchat/integrations/react-native.mdx @@ -4,11 +4,13 @@ description: Add your bot in any React Native or Expo app using react-native-web --- -**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` / `injectJavaScript`. +**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: @@ -17,185 +19,185 @@ You will need: * [`react-native-webview`](https://github.com/react-native-webview/react-native-webview) - - - To embed a bot on your mobile app, you need the bot's Webchat embed code. You can get the embed code from the 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 - - - - - 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`** | Your **`inject.js`** and embed script URLs from Studio. | - - 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/*"] - } - } +## 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. - - - - 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/). - - - - - Add **`src/config/botpressConfig.js`** with the **exact URLs** from your Studio embed snippet. Replace the placeholders with your values. - - ```javascript - /** - * Paste both script URLs from Botpress Studio → Webchat → Embed. - */ - export const botpressConfig = { - /** Your bot id for your own logs / analytics */ - botId: "YOUR-BOT-UUID", - - /** First script: cdn.botpress.cloud/.../inject.js — version must match Studio */ - injectScriptUrl: "https://cdn.botpress.cloud/webchat/v3.6/inject.js", - - /** Second script: files.bpcontent.cloud/.../....js (defer in HTML) */ - embedScriptUrl: "https://files.bpcontent.cloud/YOUR/PATH/YOUR-GENERATED.js", - - /** - * HTTPS origin used as WebView `baseUrl` (resolves relative URLs inside the page). - */ - baseUrl: "https://cdn.botpress.cloud/", - }; - ``` - - - - - Next, add **`src/botpress/getBotpressWebchat.js`**. This helper hands the WebView an **`{ html, baseUrl }`** object you can pass straight into ``. - - ```javascript getBotpressWebchat.js - /** - * Builds the HTML page loaded by the WebView for Botpress Cloud webchat v3. - * @param {Record} botConfig - Use `botpressConfig` from src/config/botpressConfig.js - * @returns {{ html: string, baseUrl: string }} - */ - const getBotpressWebchat = (botConfig) => { - const injectUrl = - botConfig.injectScriptUrl || - "https://cdn.botpress.cloud/webchat/v3.6/inject.js"; - const embedUrl = botConfig.embedScriptUrl; - if (!embedUrl || typeof embedUrl !== "string") { - throw new Error( - "botConfig.embedScriptUrl is required (paste the second script URL from Studio embed)", - ); + } +} +``` + +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 - - - - - - `; - - return { baseUrl, html }; - }; - - module.exports = getBotpressWebchat; - ``` - - - - Add **`src/botpress/BpWidget.js`**. It: - - * Loads the page from **`getBotpressWebchat(botConfig)`**. - * Injects JavaScript to subscribe to **`window.botpress`** and forward events to React Native via **`postMessage`** (`onMessage` receives JSON strings). - * Blocks top-level navigation to **`botpress.com` / `*.botpress.com`** so tapping **close** does not replace the WebView with the marketing site; it injects **`window.botpress.close()`** instead. - - ```javascript BpWidget.js expandable - /** - * WebView wrapper for Botpress webchat v3: window.botpress API, events to RN via postMessage. - */ - import { WebView } from "react-native-webview"; - import getBotpressWebchat from "./getBotpressWebchat"; - import React, { useCallback, useRef } from "react"; - - const broadcastToRn = ` + + 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 () { @@ -215,11 +217,13 @@ You will need: 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); @@ -228,8 +232,9 @@ You will need: true; `; - const closeChatJs = ` +const closeChatJs = ` try { + // Programmatic close when we block a navigation attempt (through onShouldStartLoadWithRequest). if (window.botpress && typeof window.botpress.close === "function") { window.botpress.close(); } @@ -237,126 +242,130 @@ try { true; `; - 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); - - const { html, baseUrl } = getBotpressWebchat(botConfig); - - const onShouldStartLoadWithRequest = useCallback((request) => { - const { url } = request; - if (isBotpressMarketingSiteUrl(url)) { - webref.current?.injectJavaScript(closeChatJs); - return false; - } - return true; - }, []); - - return ( - - ); - } - ``` - - - **`onMessage` format:** `event.nativeEvent.data` is a **string**. Parse with `JSON.parse`. Shape: **`{ event: string, data: unknown }`**. - - +// 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; + } +} - - If you use TypeScript, add **`src/botpress/BpWidget.d.ts`** next to **`BpWidget.js`**: +export default function BpWidget(props) { + const { botConfig, onMessage } = props; + const webref = useRef(null); - ```typescript - import type { FC } from "react"; + // Build the in-memory HTML page and origin for this WebView instance. + const { html, baseUrl } = getBotpressWebchat(botConfig); - export interface BpWidgetProps { - botConfig: Record; - onMessage?: (e: { nativeEvent: { data: string } }) => void; + // 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 ( + + ); +} +``` - declare const BpWidget: FC; - - export default BpWidget; - ``` - - - - Import **`BpWidget`** and **`botpressConfig`**, then render full-screen or inside a `View` with `flex: 1`. Adjust import paths to match your layout (Expo Router `app/index.tsx`, classic `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"; - - export default function ChatScreen() { - return ( - - - { - const raw = e.nativeEvent?.data; - if (!raw) return; - try { - const msg = JSON.parse(raw); - console.log(msg.event, msg.data); - } catch { - /* non-JSON */ - } - }} - /> - - - ); - } + + **`onMessage` format:** `event.nativeEvent.data` is a **string**. Parse with `JSON.parse`. Shape: **`{ event: string, data: unknown }`**. + - const styles = StyleSheet.create({ - safeArea: { flex: 1 }, - main: { flex: 1 }, - }); - ``` - +## 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 }, +}); +``` - - ```bash - npm install - npx expo start - ``` +## 9. Run your app - Then open the iOS Simulator, Android emulator, or a device. - - +```bash +npm install +npx expo start +``` -Your bot should load inside the WebView when you run the app. +Open the iOS Simulator, Android emulator, or a physical device from there. Your bot should load inside the WebView when you run the app. @@ -364,10 +373,10 @@ Your bot should load inside the WebView when you run the app. | Problem | What to check | |--------|----------------| -| Blank WebView | `embedScriptUrl` and `injectScriptUrl` match Studio; device online; `javaScriptEnabled` and `domStorageEnabled` are true. | +| 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 marketing website | Keep **`onShouldStartLoadWithRequest`** + **`closeChatJs`** in `BpWidget.js` as in the steps above. | -| Fonts / emoji look wrong | Tune appearance in **Botpress Studio**; avoid forcing global `font-family` in the HTML shell. | +| 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