# EmbedProvider advanced
> Advanced EmbedProvider patterns for React Native: screen visibility, route groups, delays, continuity, and FAB insets.
URL: /embed/integration/embed-provider-advanced
Markdown: /embed/integration/embed-provider-advanced.md
# EmbedProvider advanced (FAB visibility) [#embedprovider-advanced-fab-visibility]
Use this guide after you have a working **`EmbedProvider`** + **`NavigationContainer`** setup from the [React Native integration guide](/embed/integration/react-native). Here you tune **where** the FAB appears, **when** it shows, and **how** it is positioned using **`includeScreens`**, **`embedButtonDelayMs`**, and **`embedButtonVisibilityConfig`**.
***
## Introduction [#introduction]
**EmbedProvider** wraps your app and:
* Listens to **React Navigation** state and shows or hides **`EmbedButton`** by screen.
* Supports **visibility groups**: per-group delay, continuity, and inset.
* Enriches analytics with **screen context** (current screen, path, depth).
**What this guide covers**
* **`includeScreens`** - allowlist route names where the FAB may appear.
* **`embedButtonDelayMs`** - default delay before the FAB appears on an eligible screen.
* **`embedButtonVisibilityConfig`** - **groups**, **continuity**, **delay policies**, and **insets** for production-grade UX.
**Requirements**
* **React Navigation** (for example `@react-navigation/native`).
* **`EmbedProvider` must wrap `NavigationContainer`** and use the **same `ref`** you pass to **`NavigationContainer`**.
***
## Prerequisites [#prerequisites]
Before you use advanced visibility rules, confirm the following:
| Requirement | Notes |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **React Native** | 0.70 or higher (same as main integration guide) |
| **React Navigation** | Root **`NavigationContainer`** with a shared **`navigationRef`** |
| **Base embed setup** | **`useInitialize`**, **`GestureHandlerRootView`**, and **`EmbedProvider`** wired as in the main guide |
**You will also need:**
* Route **`name`** values that match your navigators exactly (**case-sensitive**).
* A clear idea of which flows should show the FAB (tabs, stacks, auth exclusions, etc.).
If **`navigationRef`** is missing or not the same ref as on **`NavigationContainer`**, the provider cannot detect screen changes and FAB visibility will not match your rules.
***
## Before you continue: base integration [#before-you-continue-base-integration]
Complete **installation**, **native LiveKit setup**, and the **Basic setup** steps in the React Native guide first. Advanced props build on that tree.
Start here if you have not finished **`useInitialize`**, peer dependencies, **`GestureHandlerRootView`**, and a minimal **`EmbedProvider`** around **`NavigationContainer`**.
***
## Basic setup (step-by-step) [#basic-setup-step-by-step]
Follow these in order. You can stop after Step 2 for a simple allowlist-only integration.
### Step 1 - Minimal provider and ref [#step-1---minimal-provider-and-ref]
```tsx
import { useRef } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { EmbedProvider } from '@revrag-ai/embed-react-native';
import { RootNavigator } from './navigation/RootNavigator';
export default function App() {
const navigationRef = useRef(null);
return (
);
}
```
* **`appVersion`** (required): Your app version string (for example from **`package.json`**). Used in analytics.
* **`navigationRef`**: Must be the ref attached to **`NavigationContainer`**. Without it, the provider cannot reliably drive screen-based visibility.
### Step 2 - Restrict screens with includeScreens (optional) [#step-2---restrict-screens-with-includescreens-optional]
```tsx
```
* **`includeScreens`**: Route **names** where the button may appear. If omitted or empty, eligibility depends on backend and groups (see visibility groups). Names must match **`Stack.Screen name="..."`** exactly.
### Step 3 - Global delay (optional) [#step-3---global-delay-optional]
Delay the first appearance of the FAB after entering an included screen:
```tsx
```
* **`embedButtonDelayMs`**: Delay in **milliseconds** before showing the FAB on an included screen. Default **`0`**. Groups can override per group.
### Step 4 - Visibility groups (optional, advanced) [#step-4---visibility-groups-optional-advanced]
For different delays, continuity, or insets per flow, configure **`embedButtonVisibilityConfig`** (see [Visibility groups](#visibility-groups) and [Configuration options](#configuration-options)).
***
## How screen-based visibility works [#how-screen-based-visibility-works]
1. You pass a **ref** from your root to both **`EmbedProvider`** and **`NavigationContainer`**.
2. The provider subscribes to navigation **state** and reads the current route (deepest active screen).
3. If the current screen is in **`includeScreens`** or in any **visibility group** when using groups, the FAB is shown; otherwise it is hidden.
**Important**
* **`EmbedProvider` must wrap `NavigationContainer`** so the listener uses the same ref.
* Route names are **case-sensitive** and must match exactly (for example **`Screen1`**, not **`screen1`**).
***
## Global delay [#global-delay]
See **Step 3** above. **`embedButtonDelayMs`** is the global default; **`embedButtonVisibilityConfig.defaultDelayMs`** and per-group **`delayMs`** override or refine behavior when you use groups.
***
## Visibility groups [#visibility-groups]
For finer control (different delays, staying visible across screens, per-group position), use **visibility groups**.
### Concepts [#concepts]
| Concept | Meaning |
| ---------------- | --------------------------------------------------------------------------------- |
| **Group** | A set of screens that share delay, continuity, and inset rules. |
| **Continuity** | Whether the FAB stays visible when moving between screens in the same group. |
| **Delay policy** | When the delay runs: every screen, once per group entry, or once per app session. |
| **Inset** | Distance from screen edges (right, bottom, and so on) for the FAB. |
### Types (import from the package) [#types-import-from-the-package]
```ts
import type {
EmbedButtonVisibilityConfig,
EmbedButtonGroupConfig,
EmbedButtonContinuity,
EmbedButtonDelayPolicy,
EmbedButtonInset,
} from '@revrag-ai/embed-react-native';
```
### `EmbedButtonVisibilityConfig` [#embedbuttonvisibilityconfig]
```ts
interface EmbedButtonVisibilityConfig {
defaultDelayMs?: number; // Optional fallback when a group does not set delayMs
defaultInset?: EmbedButtonInset;
groups?: EmbedButtonGroupConfig[];
}
```
**When do you need `defaultDelayMs`?** You do not need it if every group sets its own **`delayMs`**. It is a **fallback** when:
* A **group omits `delayMs`** - that group falls back to **`defaultDelayMs`**, then top-level **`embedButtonDelayMs`**.
* A screen is **included** (for example via **`includeScreens`**) but **does not belong to any group** - the provider uses **`defaultDelayMs`** (or **`embedButtonDelayMs`**) for that screen.
You can omit **`defaultDelayMs`** and **`defaultInset`** when every group defines its own **`delayMs`** and **`inset`**.
### `EmbedButtonGroupConfig` [#embedbuttongroupconfig]
```ts
interface EmbedButtonGroupConfig {
id: string; // Unique ID for this group
screens: string[]; // Route names in this group
continuity: EmbedButtonContinuity;
inset?: EmbedButtonInset;
delayMs?: number;
delayPolicy?: EmbedButtonDelayPolicy;
}
```
### `EmbedButtonContinuity` [#embedbuttoncontinuity]
* **`continuous`** - Moving between screens **in the same group** keeps the FAB visible; delay is **not** re-applied in the way **`perScreen`** would.
* **`perScreen`** - Each screen in the group is treated independently (delay can re-run per screen if the policy allows).
### `EmbedButtonDelayPolicy` [#embedbuttondelaypolicy]
* **`perScreen`** - Delay runs on **every** included screen in the group when you land on it.
* **`oncePerGroupEntry`** - Delay runs when **entering** the group (first screen of that visit). Moving within the group does not re-trigger the delay (pairs well with **`continuous`**).
* **`oncePerAppSession`** - Delay runs **once per app session** for that group; later visits to the group show the FAB immediately per policy.
### Including screens via groups [#including-screens-via-groups]
Screens listed in **any** group **`screens`** array count as **included** even if you omit them from **`includeScreens`**. You can:
* Use **only groups** (for example omit the allowlist and define all included screens inside **groups**), or
* Use **both** - the final included set is the **union** of **`includeScreens`** and all group screens.
***
## Insets (button position) [#insets-button-position]
**`EmbedButtonInset`** controls distance from screen edges:
```ts
type EmbedButtonInset = {
top?: number | string;
right?: number | string;
bottom?: number | string;
left?: number | string;
};
```
* Values are usually **numbers** (for example **`16`**, **`54`**).
* Set **per group** in **`EmbedButtonGroupConfig.inset`**, or a default in **`EmbedButtonVisibilityConfig.defaultInset`**.
* If unset, the SDK uses internal defaults (for example **right: 16**, **bottom: 20**).
```ts
const flowGroup: EmbedButtonGroupConfig = {
id: 'mainFlow',
screens: ['Screen1', 'Screen2', 'Screen3'],
continuity: 'continuous',
inset: { right: 16, bottom: 54 },
delayMs: 1500,
delayPolicy: 'oncePerGroupEntry',
};
```
***
## Configuration options [#configuration-options]
### `EmbedProvider` props [#embedprovider-props]
| Prop | Type | Required | Description |
| --------------------------------- | ----------------------------- | ----------- | ------------------------------------------------------------------------------- |
| **`children`** | `ReactNode` | Yes | Your app, usually **`NavigationContainer`** and below. |
| **`navigationRef`** | ref | Recommended | Same ref as **`NavigationContainer`** so route changes are observed. |
| **`appVersion`** | `string` | Yes | App version for analytics. |
| **`includeScreens`** | `string[]` | No | Route names where the FAB may appear; union with group screens if both are set. |
| **`embedButtonDelayMs`** | `number` | No | Default delay (ms) before showing the FAB when no group overrides apply. |
| **`embedButtonVisibilityConfig`** | `EmbedButtonVisibilityConfig` | No | Groups, continuity, per-group delays, insets. |
### `EmbedButtonVisibilityConfig` [#embedbuttonvisibilityconfig-1]
| Field | Description |
| -------------------- | -------------------------------------------------------------- |
| **`defaultDelayMs`** | Used when a matched group does not specify **`delayMs`**. |
| **`defaultInset`** | Default inset when a group does not specify **`inset`**. |
| **`groups`** | Array of **`EmbedButtonGroupConfig`**. |
### `EmbedButtonGroupConfig` [#embedbuttongroupconfig-1]
| Field | Description |
| ----------------- | ----------------------------------------------------------------------------------------- |
| **`id`** | Unique string for the group. |
| **`screens`** | Screen **names** in this group. |
| **`continuity`** | **`continuous`** or **`perScreen`**. |
| **`delayMs`** | Delay in ms for this group. |
| **`delayPolicy`** | **`perScreen`**, **`oncePerGroupEntry`**, or **`oncePerAppSession`**. |
| **`inset`** | Offsets from edges **`{ top, right, bottom, left }`**. |
***
## Usage examples [#usage-examples]
### Example 1: Multi-screen flow and confirmation screen [#example-1-multi-screen-flow-and-confirmation-screen]
* **Main flow**: Screen1 to Screen3. FAB after 1.5s when entering the flow, **stays visible** within the flow. Inset **right: 16**, **bottom: 54**.
* **Screen4**: Single screen. Delay 1.5s **once per app session**. Inset **right: 24**, **bottom: 32**.
* **Other screens**: No FAB.
```tsx
import {
EmbedProvider,
type EmbedButtonContinuity,
type EmbedButtonDelayPolicy,
type EmbedButtonGroupConfig,
type EmbedButtonVisibilityConfig,
} from '@revrag-ai/embed-react-native';
import { NavigationContainer } from '@react-navigation/native';
import { useRef } from 'react';
const flowGroup: EmbedButtonGroupConfig = {
id: 'mainFlow',
screens: ['Screen1', 'Screen2', 'Screen3'],
continuity: 'continuous' as EmbedButtonContinuity,
inset: { right: 16, bottom: 54 },
delayMs: 1500,
delayPolicy: 'oncePerGroupEntry' as EmbedButtonDelayPolicy,
};
const confirmationGroup: EmbedButtonGroupConfig = {
id: 'confirmationScreen',
screens: ['Screen4'],
continuity: 'perScreen' as EmbedButtonContinuity,
inset: { right: 24, bottom: 32 },
delayMs: 1500,
delayPolicy: 'oncePerAppSession' as EmbedButtonDelayPolicy,
};
const embedButtonVisibilityConfig: EmbedButtonVisibilityConfig = {
defaultDelayMs: 1200,
defaultInset: { right: 16, bottom: 20 },
groups: [flowGroup, confirmationGroup],
};
export default function App() {
const navigationRef = useRef(null);
return (
);
}
```
### Example 2: Two screens with different delays [#example-2-two-screens-with-different-delays]
* **ScreenA**: Delay 1.5s every visit, inset **right: 16**, **bottom: 24**.
* **ScreenB**: Delay 3s every visit, same inset.
```tsx
const screenAGroup: EmbedButtonGroupConfig = {
id: 'groupA',
screens: ['ScreenA'],
continuity: 'perScreen',
inset: { right: 16, bottom: 24 },
delayMs: 1500,
delayPolicy: 'perScreen',
};
const screenBGroup: EmbedButtonGroupConfig = {
id: 'groupB',
screens: ['ScreenB'],
continuity: 'perScreen',
inset: { right: 16, bottom: 24 },
delayMs: 3000,
delayPolicy: 'perScreen',
};
const embedButtonVisibilityConfig: EmbedButtonVisibilityConfig = {
defaultDelayMs: 1200,
defaultInset: { right: 16, bottom: 20 },
groups: [screenAGroup, screenBGroup],
};
```
### Example 3: Screens included only via groups [#example-3-screens-included-only-via-groups]
Omit **`includeScreens`**; only group membership decides visibility:
```tsx
```
Screens that appear in at least one group **`screens`** array get the FAB; all others do not.
***
## Practical scenarios [#practical-scenarios]
### Multi-step form flow (single delay) [#multi-step-form-flow-single-delay]
Goal: Show delay once, then keep the FAB visible across steps.
```tsx
const flowGroup: EmbedButtonGroupConfig = {
id: 'formFlow',
screens: ['Step1', 'Step2', 'Step3', 'Step4', 'Step5'],
continuity: 'continuous' as EmbedButtonContinuity,
delayMs: 1500,
delayPolicy: 'oncePerGroupEntry' as EmbedButtonDelayPolicy,
};
```
### Same flow with extra standalone screens [#same-flow-with-extra-standalone-screens]
Goal: Delay once for the flow, different behavior for other screens.
```tsx
const flowGroup: EmbedButtonGroupConfig = {
id: 'formFlow',
screens: ['Step1', 'Step2', 'Step3', 'Step4', 'Step5'],
continuity: 'continuous' as EmbedButtonContinuity,
delayMs: 1500,
delayPolicy: 'oncePerGroupEntry' as EmbedButtonDelayPolicy,
};
const otherGroup: EmbedButtonGroupConfig = {
id: 'otherScreens',
screens: ['ScreenX', 'ScreenY'],
continuity: 'perScreen' as EmbedButtonContinuity,
delayMs: 1200,
delayPolicy: 'perScreen' as EmbedButtonDelayPolicy,
};
```
### Avoid overlapping bottom UI [#avoid-overlapping-bottom-ui]
Goal: Push the FAB above a bottom tab bar.
```tsx
const flowGroup: EmbedButtonGroupConfig = {
id: 'formFlow',
screens: ['Step1', 'Step2', 'Step3'],
continuity: 'continuous' as EmbedButtonContinuity,
inset: { right: 16, bottom: 64 },
delayMs: 1200,
delayPolicy: 'oncePerGroupEntry' as EmbedButtonDelayPolicy,
};
```
***
## Troubleshooting [#troubleshooting]
| Issue | What to check |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FAB never appears | 1) **`navigationRef`** matches **`NavigationContainer`**. 2) **`EmbedProvider`** wraps **`NavigationContainer`**. 3) Current route is in **`includeScreens`** or a group **`screens`** list. |
| FAB on wrong screens | Route **`name`** values must match **exactly** (case-sensitive) your **`Stack.Screen`** (or equivalent) **`name`**. |
| Screen not interactive when FAB is visible | Overlay uses **`pointerEvents="box-none"`** so touches pass through except on the FAB. Update the SDK if behavior differs. |
| Wrong position | Set **`inset`** per group or **`defaultInset`**. Insets apply inside the FAB container on the full-screen overlay. |
| Delay feels wrong | Check **`delayPolicy`** and **`continuity`**: **`oncePerGroupEntry`** + **`continuous`** delays once on group entry; **`perScreen`** can re-apply on each screen. |
| Types not found | Import from **`@revrag-ai/embed-react-native`**: **`EmbedButtonVisibilityConfig`**, **`EmbedButtonGroupConfig`**, **`EmbedButtonContinuity`**, **`EmbedButtonDelayPolicy`**, **`EmbedButtonInset`**. |
1. Log the active route name and compare to **`includeScreens`** / group **`screens`**.
2. Confirm **`EmbedProvider`** is **outside** **`NavigationContainer`** and **`ref`** is the same object reference.
3. Temporarily set a broad **`includeScreens`** list to verify routing, then tighten.
***
## Best practices [#best-practices]
* **Match route names in config to navigator `name` props** - typos and casing break visibility.
* **Start with `includeScreens`**, then add **groups** when you need different delays or insets per flow.
* **Prefer `oncePerGroupEntry` + `continuous`** for multi-step flows so users do not see the FAB pop in on every step.
* **Tune `bottom` inset** when you have tab bars or bottom sheets so the FAB does not overlap primary UI.
* **Keep `appVersion`** accurate for analytics when debugging screen context.
* **Re-read delay policies** when QA reports "delay only happened once" - often **`oncePerAppSession`** or **`oncePerGroupEntry`** is working as designed.
***
## Support [#support]
* **Docs:** [https://docs.revrag.ai](https://docs.revrag.ai/)
* **Email:** [contact@revrag.ai](mailto:contact@revrag.ai)
**Main integration:** [React Native integration](/embed/integration/react-native) (install, native setup, **`useInitialize`**, **`USER_DATA`**, events).
***
## Related documentation [#related-documentation]
Installation, native LiveKit setup, **`GestureHandlerRootView`**, **`useInitialize`**, **`EmbedProvider`** basics, **`Embed.Event`**, and troubleshooting.