# React Dependencies > Dependency & Compatibility guide for @revrag-ai/embed-react — supported React, Next.js, router, bundler and TypeScript versions, what the SDK installs, and workarounds outside the supported ranges. URL: /embed/integration/react/react-dependencies Markdown: /embed/integration/react/react-dependencies.md # Dependency & Compatibility Guide [#dependency--compatibility-guide] For client apps integrating **`@revrag-ai/embed-react`** · SDK **1.5.0** Which React, framework, router, bundler and TypeScript versions the web SDK works with, what the SDK installs into the host app, and what to do when the host is outside the supported ranges. Everything here comes from the package's `package.json`, its build config and the example apps in the SDK repository (`examples/*`). | Status | Meaning | | ------------- | ------------------------------------------------------------------ | | **Exercised** | An example app in the SDK repository builds and runs on that stack | | **In range** | The peer ranges allow it, but no example covers it | **Treat "exercised" as strong evidence, not a release certification.** The examples build against the SDK's source through bundler aliases, not the published tarball. *** ## Table of contents [#table-of-contents] 1. [Known-good stacks](#1-known-good-stacks) — pick one, and pick the right entry 2. [Peer dependencies](#2-peer-dependencies) 3. [What the SDK installs into the host](#3-what-the-sdk-installs-into-the-host) 4. [Frameworks, routers and bundlers](#4-frameworks-routers-and-bundlers) 5. [Outside the ranges: workarounds](#5-outside-the-ranges-workarounds) 6. [TypeScript](#6-typescript) 7. [Browsers and runtime](#7-browsers-and-runtime) 8. [Package managers](#8-package-managers) 9. [Out of scope](#9-out-of-scope) *** ## 1. Known-good stacks [#1-known-good-stacks] Pin to one of these when you can. Each row is an example app in the SDK repository. | Stack | React | Framework / bundler | Router | TypeScript | SDK entry | Example | | ------------------------- | ---------------------------------------- | ----------------------------------------- | ----------------------------------- | ---------------------------- | ----------------------- | ------------------------ | | **A. React 19 SPA** | `react`/`react-dom` **19.2.x** | Vite **7.3** + `@vitejs/plugin-react` 5.1 | `react-router-dom` **7.x** (7.13.1) | 5.9 (`bundler`) | default | `examples/react` | | **B. Next.js App Router** | **19.2.3** | Next.js **16.1.6** | Next (App Router) | 5.x (`bundler`) | default | `examples/nextjs` | | **C. React 17 SPA** | **17.0.2** | Vite **7.3** | none / any | — | default (works on Vite) | `examples/react17-smoke` | | **D. React 17 + Next.js** | **17.0.2** | Next.js **12.3.4** (webpack 5) | Next (Pages Router) | **4.9.5** (`node` + `paths`) | **`/react17`** | `examples/next17-smoke` | | **E. Preact** | `preact` **10.29.x** via `preact/compat` | Vite **7.3** + `@preact/preset-vite` 2.10 | `preact-router` 4.1.2 | (`bundler`) | default | `examples/preact-poc` | **Pick the entry by the host's React version and bundler:** | Host | Entry to import from | | --------------------------------------------------------- | -------------------------------- | | React 18 / 19, any bundler | `@revrag-ai/embed-react` | | React 17 on Vite (Rollup) | `@revrag-ai/embed-react` | | React 17 on **webpack** (CRA, Next.js 12, custom webpack) | `@revrag-ai/embed-react/react17` | | Preact 10 (Vite preset, or webpack with aliases) | `@revrag-ai/embed-react` | **Why webpack + React 17 needs its own entry.** * The default entry imports `framer-motion` 12, which statically imports `useId` / `useInsertionEffect` from `react`. Those hooks only exist in React 18+. * Webpack checks named imports at build time and fails with `Attempted import error`; Rollup/Vite only warns. * The `/react17` entry is the same code with `framer-motion` swapped for a built-in stub, so it never touches those imports. *** ## 2. Peer dependencies [#2-peer-dependencies] | Package | Declared range | Required | Status | | ------------------ | -------------- | -------- | -------------------------------------------------------------------------------------------------------------- | | `react` | `>=17.0.0` | yes | 17 (A/C/D), 19 (A/B) exercised; 18 in range | | `react-dom` | `>=17.0.0` | yes | same as `react`; must be the same version | | `next` | `>=14.0.0` | optional | 16 exercised; 14, 15 in range; **12 exercised but out of range** (see [§5](#5-outside-the-ranges-workarounds)) | | `react-router-dom` | `>=6.0.0` | optional | 7 exercised; 6 in range; **5 out of range** (see [§5](#5-outside-the-ranges-workarounds)) | * **`next` and `react-router-dom` are optional peers.** The published bundles never import either one: the host passes the current path to `EmbedProvider` as a string. * **Any router works**, including ones not listed (TanStack Router, `preact-iso`, `wouter`, a hand-rolled state machine). *** ## 3. What the SDK installs into the host [#3-what-the-sdk-installs-into-the-host] These are runtime `dependencies` of `@revrag-ai/embed-react`, so npm installs them into the host. **Do not add any of them to the host's `package.json` for the SDK.** | Package | Range | Used by | If the host already has it | | --------------------- | ---------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `livekit-client` | `^2.6.0` | voice calls. Loaded with a dynamic `import()` **only when a call starts** | Host on 2.x ⇒ npm shares one copy (fine). Host on 1.x ⇒ the SDK gets its own nested 2.x copy (fine, larger install) | | `framer-motion` | `^12.23.9` | widget animations. **Default entry only**; the `/react17` entry never imports it | Host on 12.x ⇒ shared. Host on ≤ 11 ⇒ a second nested copy, which costs bundle size but doesn't conflict. Host using the `motion` package ⇒ separate, no conflict | | `zustand` | `^4.5.5` | internal state | Host on zustand 5 ⇒ nested copy, independent stores, no conflict | | `zod` | `^3.24.1` | validating backend/agent messages | Host on zod 4 ⇒ nested copy, no conflict | | `zod-to-json-schema` | `^3.23.5` | schema helpers | — | | `clsx` | `^2.1.1` | class names | — | | `tailwind-merge` | `^2.5.4` | class names | Host on 3.x ⇒ nested copy, no conflict | | `tailwindcss-animate` | `^1.0.7` | listed, build-time only | The host does **not** need Tailwind | | `tiny-invariant` | `^1.3.3` | assertions | — | **Bundled inside the package** (not installed, cannot conflict): * **`@revrag-ai/embed-core`** — shipped in `internal/core`. Never install or import it. * **`lottie-web` (5.13)** — bundled into its own chunk (`lottie-*.js`, \~420 KB). * The chunk is split out, but the default avatar animation itself is in the main bundle, so expect a noticeable first-load cost. * A host's own `lottie-web` / `lottie-react` is unaffected. **Stylesheet.** `@revrag-ai/embed-react/style.css` (\~32 KB) is precompiled. * **Every rule is scoped** to the widget's own `embed-*` / `ai-widget-*` classes; there is no global reset. * **It works with or without** Tailwind, CSS Modules, styled-components, Emotion or MUI in the host. **Check the result after installing:** ```bash npm ls @revrag-ai/embed-react # 1.5.0 npm ls react react-dom # exactly ONE copy each npm ls livekit-client --all # one 2.x copy is ideal; a nested second copy is acceptable npm ls framer-motion --all # default entry only ``` **Two copies of `react` / `react-dom` are never acceptable.** They give `Invalid hook call`. * This usually happens in monorepos, with linked packages, or with pnpm hoisting. * Dedupe them: `npm dedupe`, `resolutions` / `overrides`, or a bundler alias to the app's copy. *** ## 4. Frameworks, routers and bundlers [#4-frameworks-routers-and-bundlers] ### React [#react] | Version | Status | Notes | | ------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 19.x | exercised (A, B) | | | 18.x | in range | Same code path as 19 | | 17.x | exercised (C, D) | `useSyncExternalStore` is polyfilled internally; animations are simpler (static styles instead of motion), which is expected. Add no polyfills. Webpack hosts must use `/react17` | | ≤ 16 | **unsupported** | Hooks-only SDK; peer range excludes it | ### Next.js [#nextjs] | Version | Router | Status | Notes | | ------- | ----------- | -------------------------------- | -------------------------------------------------------------------------------------------------- | | 16 | App Router | exercised (B) | The package ships `"use client"`; `useInitialize` / `usePathname` still go in one client component | | 14, 15 | App / Pages | in range | Same integration as 16 | | 13 | App / Pages | out of peer range | Install with `--legacy-peer-deps`. The SDK never imports `next`, so this is safe | | 12 | Pages | exercised (D), out of peer range | React 17 ⇒ `/react17` entry; `--legacy-peer-deps` | | ≤ 11 | — | untested | webpack 4 era; not supported | For the Pages Router, pass `router.pathname` (the route pattern), never `router.asPath`. ### Routers [#routers] | Router | Status | What to pass as `currentPath` | | ------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------- | | `react-router-dom` 7 | exercised (A) | `useLocation().pathname` | | `react-router-dom` 6 | in range | `useLocation().pathname` | | `react-router-dom` 5 | out of peer range (`--legacy-peer-deps`); the SDK doesn't import it | `useLocation().pathname` / `withRouter` `location.pathname` | | Next.js App Router | exercised (B) | `usePathname()` | | Next.js Pages Router | exercised (D) | `useRouter().pathname` | | `preact-router` 4 | exercised (E) | current URL's pathname | | `preact-iso` | in range | `useLocation().path` (inside ``) | | TanStack Router, wouter, others | works through the prop | the router's current pathname | | No router | works through the prop | a synthetic path per screen, e.g. `/onboarding/${step}` | ### Bundlers [#bundlers] | Bundler | Status | Notes | | ----------------------------------------- | ------------------- | ------------------------------------------------------------------- | | Vite 7 | exercised (A, C, E) | | | Vite 5–6 | in range | | | webpack 5 (Next.js 12+) | exercised (D) | React 17 ⇒ `/react17` | | webpack 5 (CRA `react-scripts` 5, custom) | in range | React 17 ⇒ `/react17` | | Turbopack (Next.js 15/16 dev) | in range | | | Rspack, Parcel, esbuild | untested | Should work: plain ESM + CJS, standard `exports` map. Report issues | | webpack 4 | **unsupported** | Doesn't understand the package `exports` map | **The package publishes both ESM (`index.js`) and CJS (`index.cjs`) behind an `exports` map.** Any bundler that ignores `exports` can't resolve `/react17`, `/diagnostics` or `/style.css`. ### Preact [#preact] `preact` 10 through `preact/compat`. * **Vite + `@preact/preset-vite`:** nothing to configure. * **webpack:** alias `react`, `react-dom`, `react-dom/test-utils` and `react/jsx-runtime` to `preact/compat` / `preact/test-utils` / `preact/jsx-runtime`. * **npm may install `react` / `react-dom`** to satisfy the peers. With the aliases in place they never reach the bundle. *** ## 5. Outside the ranges: workarounds [#5-outside-the-ranges-workarounds] | Host | Symptom | Do | | ---------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | Next.js 12 or 13 | `npm ERR! ERESOLVE` on install | `npm install --legacy-peer-deps` (or an `overrides` entry). Safe: the SDK never imports `next` | | React Router 5 | same `ERESOLVE` | same workaround | | React 17 on webpack, default entry | `Attempted import error: 'useId' is not exported from 'react'` (from framer-motion) | Import everything from `@revrag-ai/embed-react/react17` | | TS `moduleResolution: "node"` | `Cannot find module '@revrag-ai/embed-react/react17'` (or `/diagnostics`) | Switch to `bundler` / `nodenext`, or add `compilerOptions.paths` (below) | | Two React copies | `Invalid hook call` | Dedupe (see [§3](#3-what-the-sdk-installs-into-the-host)) | | yarn berry with PnP | resolution errors in SDK dependencies | `nodeLinker: node-modules` in `.yarnrc.yml` | | pnpm `strict-peer-dependencies` | install fails on optional peers | Leave it off, or use `peerDependencyRules.allowedVersions` | The `compilerOptions.paths` entries for `moduleResolution: "node"`: ```json { "compilerOptions": { "paths": { "@revrag-ai/embed-react/react17": ["./node_modules/@revrag-ai/embed-react/index.d.ts"], "@revrag-ai/embed-react/diagnostics": ["./node_modules/@revrag-ai/embed-react/diagnostics.d.ts"] } } } ``` **Do not up- or downgrade the host's unrelated dependencies to fit the SDK.** Every mismatch above has a workaround that leaves the host's versions alone. *** ## 6. TypeScript [#6-typescript] | | | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | Versions | 4.9 (D) and 5.x (A, B) exercised | | `moduleResolution` | `bundler` or `nodenext` recommended. `node` (node10) needs the `paths` entries in [§5](#5-outside-the-ranges-workarounds) for subpaths | | Types | Bundled (`index.d.ts`, `diagnostics.d.ts`). No `@types` package to install | | `@types/react` | Use the one that matches the host's React (17.x types for React 17). The SDK doesn't force one | *** ## 7. Browsers and runtime [#7-browsers-and-runtime] | Requirement | Detail | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | JavaScript | ES2020 output (Vite's default library target: Chrome 87, Edge 88, Firefox 78, Safari 14). Older browsers need the host to transpile `node_modules`, and are untested | | Calls | WebRTC + `getUserMedia`, in a **secure context** (HTTPS, or `localhost`) | | Storage | `sessionStorage` is **required**: the widget doesn't render without it. `localStorage` is optional. IndexedDB is optional (falls back to memory) | | Test on | Desktop Chrome and iOS Safari, on the production build. iOS Safari only allows the mic and audio after a user gesture | | SSR | Safe to import on the server: nothing touches `window` at import. Hooks must run in client components | *** ## 8. Package managers [#8-package-managers] | Manager | Notes | | ------------ | --------------------------------------------------------------------------------------------------------- | | npm 7+ | Installs peers automatically. Expect `ERESOLVE` only in the [§5](#5-outside-the-ranges-workarounds) cases | | yarn classic | Doesn't install peers: make sure `react` / `react-dom` are present (they are in any React app) | | yarn berry | Use `nodeLinker: node-modules` | | pnpm | Works; check `pnpm why react` for a single copy in workspaces | *** ## 9. Out of scope [#9-out-of-scope] * **Angular** has its own package, `@revrag-ai/embed-angular` — see the [Angular integration guide](/embed/integration/angular). * **Vue** isn't supported by this package. * **React Native** has its own package, `@revrag-ai/embed-react-native` — see the [React Native integration guide](/embed/integration/react-native). * **Plain `