# React (Web) Copilot Integration > Master prompt for an AI coding agent (Copilot / Cursor / Claude Code) to integrate @revrag-ai/embed-react end-to-end. URL: /embed/llmText/react-copilotintegration Markdown: /embed/llmText/react-copilotintegration.md # RevRag React (Web) SDK — AI Agent Integration Master Prompt [#revrag-react-web-sdk--ai-agent-integration-master-prompt] > **How to use:** paste this entire document into your AI coding agent (Copilot, Cursor, > Claude Code, etc.) inside the web app you want to integrate — React SPA, Next.js, > React 17 or Preact. The agent will inspect your app, install and wire > `@revrag-ai/embed-react`, ask you the few decisions only you can make, then validate > the integration end-to-end and report. > > Every rule below is derived from the SDK's source (v1.5.0). Where this prompt and a > generic React tutorial disagree, this prompt is correct for **this** SDK. Versions up > to 1.4.4 predate most of it — do not apply it to them. *** You are an **expert web integration agent**. Take this application from **zero → working → validated** RevRag SDK integration with minimal manual work. **Prime directives** 1. **Inspect first, modify second.** Never assume the framework, router, React version, bundler, package manager, or hosting setup. Read the project. 2. **Preserve host behavior.** Merge into the existing root, layout, providers, bundler config and CSP; never overwrite `main.tsx`, `app/layout.tsx`, `_app.tsx`, `vite.config`, `next.config` or `tsconfig` blindly. 3. **Never claim success without running the validation** in §16–§21 and observing the log lines and the `verifyIntegration()` result named there. 4. **Ask the developer** for the decisions in §13 — do not guess them. 5. **Never hide an error.** Report SDK-side issues you cannot fix. 6. **Use only the public API.** Never import from `@revrag-ai/embed-core`, `@revrag-ai/embed-react/dist/...` or any internal path. Do not add dependencies, polyfills or config the SDK does not require. *** ## 1. Inspect the existing application [#1-inspect-the-existing-application] Record each of these before changing anything: | What | How to find it | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | React / React DOM version | `package.json`; `npm ls react react-dom` | | Preact? | `preact` in deps; `@preact/preset-vite` in `vite.config.*`; `react` aliased to `preact/compat` | | Framework | `next.config.*` ⇒ Next.js. Then: `app/layout.tsx` ⇒ **App Router**; `pages/_app.tsx` ⇒ **Pages Router** (both ⇒ App Router is primary). `angular.json` ⇒ **stop**, use the Angular SDK | | Next.js version | `npm ls next` (12/13 need `--legacy-peer-deps`) | | Bundler | Vite, webpack (CRA, Next ≤ 12, custom), Rspack, Parcel — decides the React 17 entry (§2) | | Router | `react-router-dom` (version; `BrowserRouter` / `HashRouter` / `createBrowserRouter`; `basename`?), Next router, `preact-iso`, `preact-router`, TanStack Router, or none / state-driven | | Where the router mounts | Is it conditional (auth gate, suspense, feature flag)? Is there a persistent layout/shell that never unmounts? | | Root component / entry | `src/main.tsx` / `src/index.tsx`, `app/layout.tsx`, `pages/_app.tsx` | | React roots | `grep -rn "createRoot\|ReactDOM.render\|hydrateRoot"` — more than one ⇒ micro-frontends (§22) | | `StrictMode` | in the entry file (affects §19 in dev only) | | TypeScript | `tsconfig.json` `compilerOptions.moduleResolution` (`node` needs a `paths` entry for `/react17`) | | Package manager | `package-lock.json` / `yarn.lock` (+ `.yarnrc.yml` ⇒ berry) / `pnpm-lock.yaml` | | Env/config system | `import.meta.env.VITE_*`, `process.env.NEXT_PUBLIC_*`, `process.env.REACT_APP_*`, runtime config | | App version source | `package.json` `version`, a build id, CI env var, `next.config` `env` | | Auth flow | where login completes (user id becomes known) and where sign-out happens | | CSP / headers | ``, `next.config` `headers()`, server/CDN config, `Permissions-Policy` | | Hosting | HTTPS everywhere? Served inside an iframe? | | Existing overlays / widgets | chat widgets, toasts, cookie banners, sticky footers, other fixed-position floating buttons | | Existing LiveKit / WebRTC | `npm ls livekit-client` | | Component library | MUI, Radix, shadcn, Chakra, Ant, Headless UI, Ionic / Shoelace / Stencil (web components) | | Sensitive screens / fields | login, OTP, PIN, payment, KYC, account numbers | *** ## 2. Determine compatibility [#2-determine-compatibility] **Supported:** React **17, 18, 19**; **Preact 10** via `preact/compat`; Next.js **App Router and Pages Router**; any current browser with WebRTC and `getUserMedia`; a **secure context** (HTTPS, or `localhost` in development). **Pick the entry point** — the API is identical; only the import path changes: | Host | Import from | | ----------------------------------------------------------------- | -------------------------------- | | React 18 / 19, any bundler | `@revrag-ai/embed-react` | | React 17 on **Vite** | `@revrag-ai/embed-react` | | React 17 on **webpack** (CRA, Next.js 12, custom) | `@revrag-ai/embed-react/react17` | | Preact (Vite with `@preact/preset-vite`, or webpack with aliases) | `@revrag-ai/embed-react` | Use **one** entry consistently across the whole app. Diagnostics always come from `@revrag-ai/embed-react/diagnostics`. **Peer ranges:** | Package | Range | Notes | | -------------------- | ---------- | --------------------------------------------------------------------------------------------------- | | `react`, `react-dom` | `>=17.0.0` | required | | `next` | `>=14.0.0` | optional; the SDK never imports it. Next **12/13** ⇒ `ERESOLVE` — install with `--legacy-peer-deps` | | `react-router-dom` | `>=6.0.0` | optional; the SDK never imports it. React Router **5** ⇒ same `ERESOLVE` workaround | **TypeScript:** `moduleResolution: "bundler"` or `"nodenext"`. With `"node"`, the `/react17` subpath needs a `paths` entry (§7). **Do not upgrade or downgrade unrelated dependencies** to make the SDK fit. If something is incompatible, explain it, apply the workaround above, and re-validate. *** ## 3. Install the SDK [#3-install-the-sdk] ```bash npm install @revrag-ai/embed-react@^1.5.0 # yarn add @revrag-ai/embed-react@^1.5.0 / pnpm add @revrag-ai/embed-react@^1.5.0 # Next.js 12–13 or React Router 5: add --legacy-peer-deps ``` If given a **local tarball**, install that path instead. Then verify: ```bash npm ls @revrag-ai/embed-react # 1.5.0 or later ls node_modules/@revrag-ai/embed-react/{index.js,index.react17.js,diagnostics.js,style.css} npm ls react react-dom # exactly ONE copy each (monorepo / pnpm hoisting!) npm ls livekit-client --all # the SDK brings its own; note any second copy from the host ``` **Never add `livekit-client`, `framer-motion` or `lottie-web` to the app for the SDK** — they are the SDK's own dependencies. `livekit-client` loads only when a call starts. *** ## 4. Configure the bundler and stylesheet [#4-configure-the-bundler-and-stylesheet] ### Stylesheet (required) [#stylesheet-required] Import once, at the app's entry point: ```ts import '@revrag-ai/embed-react/style.css'; ``` | Framework | File | | ----------------- | --------------------------------------------------- | | React SPA | `src/main.tsx` / `src/index.tsx` | | Next App Router | `app/layout.tsx` (a Server Component may import it) | | Next Pages Router | `pages/_app.tsx` | The path is exactly `@revrag-ai/embed-react/style.css`. **`…/dist/style.css` does not resolve** from the published package. Without the stylesheet the button gets only a minimal fallback and looks broken. ### Preact [#preact] * **Vite + `@preact/preset-vite`**: nothing to do; the preset aliases `react`, `react-dom` and `react/jsx-runtime` to `preact/compat`. * **webpack / others**: add to `resolve.alias`: ```js react: 'preact/compat', 'react-dom/test-utils': 'preact/test-utils', 'react-dom': 'preact/compat', 'react/jsx-runtime': 'preact/jsx-runtime', ``` * npm may install `react`/`react-dom` to satisfy peers. Harmless — the alias keeps them out of the bundle. Do not "fix" it. ### Package-manager notes [#package-manager-notes] * **yarn berry with PnP**: if resolution of the SDK's dependencies fails, switch to `nodeLinker: node-modules` and tell the developer. * **pnpm**: do not enable `strict-peer-dependencies` for this; pin the SDK version. * **Monorepos**: the app must resolve **one** `react`/`react-dom`. Two copies give "Invalid hook call". *** ## 5. Mount: framework-specific integration [#5-mount-framework-specific-integration] Three steps for everyone: **initialize** once (`useInitialize`), **mount** one provider (`EmbedProvider`), **identify** the user after login (§11). Pick the block that matches §1. ### 5A. React SPA with React Router (Vite / CRA) [#5a-react-spa-with-react-router-vite--cra] ```tsx // src/main.tsx import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import { BrowserRouter, useLocation } from 'react-router-dom'; import { EmbedProvider, useInitialize } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; const API_KEY = import.meta.env.VITE_REVRAG_API_KEY as string; const APP_VERSION = import.meta.env.VITE_APP_VERSION as string; // Module scope — never inline visibility config in JSX (§14). const EXCLUDED = ['/login', '/otp']; // The provider lives INSIDE the router so it can read the path. function RevragShell({ children }: { children: React.ReactNode }) { const { pathname } = useLocation(); return ( {children} ); } // Initialize once, in a component that never unmounts, at or above the provider. function App() { const { error } = useInitialize(API_KEY, { appVersion: APP_VERSION }); if (error) console.error('[Revrag] init failed:', error); return ( ); } createRoot(document.getElementById('root')!).render(); ``` * `HashRouter`: `useLocation().pathname` is the part after `#` — pass it as-is. * Data routers (`createBrowserRouter` + `RouterProvider`): put `RevragShell` in the root route's layout element (it must be inside the router), keep `useInitialize` above `RouterProvider`. * `useLocation()` throws outside the router — `RevragShell` must sit inside it. ### 5B. Next.js App Router [#5b-nextjs-app-router] ```tsx // app/revrag-root.tsx 'use client'; import type { ReactNode } from 'react'; import { usePathname } from 'next/navigation'; import { EmbedProvider, useInitialize } from '@revrag-ai/embed-react'; const API_KEY = process.env.NEXT_PUBLIC_REVRAG_API_KEY as string; const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION as string; const EXCLUDED = ['/login']; export function RevragRoot({ children }: { children: ReactNode }) { useInitialize(API_KEY, { appVersion: APP_VERSION }); const pathname = usePathname(); return ( {children} ); } ``` ```tsx // app/layout.tsx — Server Component; merge into the existing layout import '@revrag-ai/embed-react/style.css'; import { RevragRoot } from './revrag-root'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` * **Root layout only.** A provider in a page or nested layout remounts on navigation and ends a live call. * The package ships `"use client"`, but `useInitialize` and `usePathname` are hooks, so this one client component is still required. Never call them from a Server Component. * Expose the version at build time, e.g. `next.config`: `env: { NEXT_PUBLIC_APP_VERSION: require('./package.json').version }`. ### 5C. Next.js Pages Router [#5c-nextjs-pages-router] ```tsx // pages/_app.tsx — merge into the existing _app import type { AppProps } from 'next/app'; import { useRouter } from 'next/router'; import { EmbedProvider, useInitialize } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; const EXCLUDED = ['/login']; export default function MyApp({ Component, pageProps }: AppProps) { useInitialize(process.env.NEXT_PUBLIC_REVRAG_API_KEY as string, { appVersion: process.env.NEXT_PUBLIC_APP_VERSION as string, }); const router = useRouter(); return ( // router.pathname is the route PATTERN ('/orders/[id]') — ideal for screen lists. ); } ``` Use **`router.pathname`, never `router.asPath`** — `asPath` carries query and hash and never matches the lists. On Next.js 12 + React 17, import from `/react17`. ### 5D. React 17 [#5d-react-17] Same code as 5A/5C. On **webpack** hosts, every SDK import comes from `@revrag-ai/embed-react/react17` (it swaps `framer-motion` for a built-in fallback). On Vite, the default entry works. Animations may be simpler — expected; add no polyfills. ### 5E. Preact [#5e-preact] §4 aliases, then 5A. Pass the router's path: `preact-iso` → `useLocation().path` (`EmbedProvider` must sit inside ``); `preact-router` → its current URL pathname. ### 5F. No router (tabs, steps, wizards in state) [#5f-no-router-tabs-steps-wizards-in-state] Give each state a synthetic, privacy-safe path: ```tsx ``` Never put a user id, account number or token in a path. ### Rules for every shape [#rules-for-every-shape] * **`EmbedProvider` renders the button itself.** Never also render `` — that mounts a second widget with a second call manager. * **Exactly one provider per page.** Two React roots or a micro-frontend mounting its own ⇒ two widgets that drift apart. * **Always pass `currentPath`** (or `usePathHook`). Without it the SDK reads `window.location.pathname` and only re-reads it on back/forward, so client-side navigations are missed. * `useInitialize` must be in a component that **stays mounted** and is **at or above** the provider. Unmounting it stops tracking. *** ## 6. Hosting, CSP and microphone [#6-hosting-csp-and-microphone] The SDK requests the microphone itself on the first call — **write no permission flow**. Make sure the browser *can* ask: 1. **HTTPS** everywhere outside `localhost`. On HTTP the call fails with no prompt. 2. **Inside an iframe**: the frame needs `allow="microphone"`, and no `Permissions-Policy` header on the parent may block it. 3. **CSP** — if the app sends one, add (merge, never replace): | Directive | Allow | Why | | ------------- | -------------------------------------------------------------------------------- | ------------------------------- | | `connect-src` | `https://embed.revrag.ai` (or the `baseUrl` host) | init, events, token, screen map | | `connect-src` | the call server over **both** `wss:` and `https:` — ask RevRag for the host | WebRTC signalling | | `connect-src` | the avatar animation host, and `https://corsproxy.io` | avatar fetch + its fallback | | `img-src` | `data:`, `https://revrag-dev.s3.ap-south-1.amazonaws.com`, the avatar image host | bundled avatar, in-call icons | | `script-src` | `'unsafe-eval'` **only** if the avatar uses Lottie expressions | otherwise not needed | | `style-src` | `'unsafe-inline'` **only** if the stylesheet is not imported | fallback `