# 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 &#x2A;*`EmbedProvider`*&#x2A; + &#x2A;*`NavigationContainer`** setup from the [React Native integration guide](/embed/integration/react-native). Here you tune **where** the FAB appears, **when** it shows, and **how*&#x2A; it is positioned using &#x2A;*`includeScreens`*&#x2A;, &#x2A;*`embedButtonDelayMs`*&#x2A;, and &#x2A;*`embedButtonVisibilityConfig`**.

***

## Introduction [#introduction]

**EmbedProvider** wraps your app and:

* Listens to **React Navigation*&#x2A; state and shows or hides &#x2A;*`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 &#x2A;*same `ref`*&#x2A; you pass to &#x2A;*`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 &#x2A;*`NavigationContainer`*&#x2A; with a shared &#x2A;*`navigationRef`**                                           |
| **Base embed setup** | **`useInitialize`*&#x2A;, &#x2A;*`GestureHandlerRootView`*&#x2A;, and &#x2A;*`EmbedProvider`** wired as in the main guide |

**You will also need:**

* Route &#x2A;*`name`** values that match your navigators exactly (**case-sensitive**).
* A clear idea of which flows should show the FAB (tabs, stacks, auth exclusions, etc.).

<Note>
  If &#x2A;*`navigationRef`*&#x2A; is missing or not the same ref as on &#x2A;*`NavigationContainer`**, the provider cannot detect screen changes and FAB visibility will not match your rules.
</Note>

***

## 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.

<CardGroup cols="1">
  <Card title="React Native integration (install, native setup, basic EmbedProvider)" href="/embed/integration/react-native">
    Start here if you have not finished &#x2A;*`useInitialize`*&#x2A;, peer dependencies, &#x2A;*`GestureHandlerRootView`*&#x2A;, and a minimal &#x2A;*`EmbedProvider`*&#x2A; around &#x2A;*`NavigationContainer`**.
  </Card>
</CardGroup>

***

## 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 (
    <EmbedProvider navigationRef={navigationRef} appVersion="1.0.0">
      <NavigationContainer ref={navigationRef}>
        <RootNavigator />
      </NavigationContainer>
    </EmbedProvider>
  );
}
```

* **`appVersion`*&#x2A; (required): Your app version string (for example from &#x2A;*`package.json`**). Used in analytics.
* **`navigationRef`*&#x2A;: Must be the ref attached to &#x2A;*`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
<EmbedProvider
  navigationRef={navigationRef}
  appVersion="1.0.0"
  includeScreens={['ScreenA', 'ScreenB', 'ScreenC']}
>
  <NavigationContainer ref={navigationRef}>
    <RootNavigator />
  </NavigationContainer>
</EmbedProvider>
```

* **`includeScreens`**: Route **names*&#x2A; where the button may appear. If omitted or empty, eligibility depends on backend and groups (see visibility groups). Names must match &#x2A;*`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
<EmbedProvider
  navigationRef={navigationRef}
  appVersion="1.0.0"
  includeScreens={['ScreenA', 'ScreenB']}
  embedButtonDelayMs={1500}
>
  <NavigationContainer ref={navigationRef}>
    <RootNavigator />
  </NavigationContainer>
</EmbedProvider>
```

* **`embedButtonDelayMs`**: Delay in **milliseconds*&#x2A; before showing the FAB on an included screen. Default &#x2A;*`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 &#x2A;*`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*&#x2A; from your root to both &#x2A;*`EmbedProvider`*&#x2A; and &#x2A;*`NavigationContainer`**.
2. The provider subscribes to navigation **state** and reads the current route (deepest active screen).
3. If the current screen is in &#x2A;*`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*&#x2A; and must match exactly (for example &#x2A;*`Screen1`*&#x2A;, not &#x2A;*`screen1`**).

***

## Global delay [#global-delay]

See **Step 3*&#x2A; above. &#x2A;*`embedButtonDelayMs`*&#x2A; is the global default; &#x2A;*`embedButtonVisibilityConfig.defaultDelayMs`*&#x2A; and per-group &#x2A;*`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`?*&#x2A; You do not need it if every group sets its own &#x2A;*`delayMs`**. It is a **fallback** when:

* A &#x2A;*group omits `delayMs`*&#x2A; - that group falls back to &#x2A;*`defaultDelayMs`*&#x2A;, then top-level &#x2A;*`embedButtonDelayMs`**.
* A screen is **included*&#x2A; (for example via &#x2A;*`includeScreens`**) but **does not belong to any group*&#x2A; - the provider uses &#x2A;*`defaultDelayMs`*&#x2A; (or &#x2A;*`embedButtonDelayMs`**) for that screen.

You can omit &#x2A;*`defaultDelayMs`*&#x2A; and &#x2A;*`defaultInset`*&#x2A; when every group defines its own &#x2A;*`delayMs`*&#x2A; and &#x2A;*`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*&#x2A; re-applied in the way &#x2A;*`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*&#x2A; the group (first screen of that visit). Moving within the group does not re-trigger the delay (pairs well with &#x2A;*`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*&#x2A; group &#x2A;*`screens`** array count as **included*&#x2A; even if you omit them from &#x2A;*`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*&#x2A; of &#x2A;*`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*&#x2A; (for example &#x2A;*`16`*&#x2A;, &#x2A;*`54`**).
* Set **per group*&#x2A; in &#x2A;*`EmbedButtonGroupConfig.inset`*&#x2A;, or a default in &#x2A;*`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 &#x2A;*`NavigationContainer`** and below.                     |
| **`navigationRef`**               | ref                           | Recommended | Same ref as &#x2A;*`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 &#x2A;*`delayMs`**. |
| **`defaultInset`**   | Default inset when a group does not specify &#x2A;*`inset`**.  |
| **`groups`**         | Array of &#x2A;*`EmbedButtonGroupConfig`**.                    |

### `EmbedButtonGroupConfig` [#embedbuttongroupconfig-1]

| Field             | Description                                                                               |
| ----------------- | ----------------------------------------------------------------------------------------- |
| **`id`**          | Unique string for the group.                                                              |
| **`screens`**     | Screen **names** in this group.                                                           |
| **`continuity`**  | **`continuous`*&#x2A; or &#x2A;*`perScreen`**.                                            |
| **`delayMs`**     | Delay in ms for this group.                                                               |
| **`delayPolicy`** | **`perScreen`*&#x2A;, &#x2A;*`oncePerGroupEntry`*&#x2A;, or &#x2A;*`oncePerAppSession`**. |
| **`inset`**       | Offsets from edges &#x2A;*`{ 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 (
    <EmbedProvider
      navigationRef={navigationRef}
      includeScreens={['Screen1', 'Screen2', 'Screen3', 'Screen4']}
      appVersion="1.0.0"
      embedButtonDelayMs={1200}
      embedButtonVisibilityConfig={embedButtonVisibilityConfig}
    >
      <NavigationContainer ref={navigationRef}>
        <RootNavigator />
      </NavigationContainer>
    </EmbedProvider>
  );
}
```

### 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],
};

<EmbedProvider
  navigationRef={navigationRef}
  includeScreens={['ScreenA', 'ScreenB']}
  appVersion="1.0.0"
  embedButtonVisibilityConfig={embedButtonVisibilityConfig}
>
  <NavigationContainer ref={navigationRef}>
    <RootNavigator />
  </NavigationContainer>
</EmbedProvider>
```

### Example 3: Screens included only via groups [#example-3-screens-included-only-via-groups]

Omit &#x2A;*`includeScreens`**; only group membership decides visibility:

```tsx
<EmbedProvider
  navigationRef={navigationRef}
  appVersion="1.0.0"
  embedButtonVisibilityConfig={embedButtonVisibilityConfig}
>
  <NavigationContainer ref={navigationRef}>
    <RootNavigator />
  </NavigationContainer>
</EmbedProvider>
```

Screens that appear in at least one group &#x2A;*`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) &#x2A;*`navigationRef`*&#x2A; matches &#x2A;*`NavigationContainer`*&#x2A;. 2) &#x2A;*`EmbedProvider`*&#x2A; wraps &#x2A;*`NavigationContainer`*&#x2A;. 3) Current route is in &#x2A;*`includeScreens`*&#x2A; or a group &#x2A;*`screens`** list.         |
| FAB on wrong screens                       | Route &#x2A;*`name`** values must match **exactly*&#x2A; (case-sensitive) your &#x2A;*`Stack.Screen`*&#x2A; (or equivalent) &#x2A;*`name`**.                                                                                                                |
| Screen not interactive when FAB is visible | Overlay uses &#x2A;*`pointerEvents="box-none"`** so touches pass through except on the FAB. Update the SDK if behavior differs.                                                                                                                             |
| Wrong position                             | Set &#x2A;*`inset`*&#x2A; per group or &#x2A;*`defaultInset`**. Insets apply inside the FAB container on the full-screen overlay.                                                                                                                           |
| Delay feels wrong                          | Check &#x2A;*`delayPolicy`*&#x2A; and &#x2A;*`continuity`*&#x2A;: &#x2A;*`oncePerGroupEntry`*&#x2A; + &#x2A;*`continuous`*&#x2A; delays once on group entry; &#x2A;*`perScreen`** can re-apply on each screen.                                              |
| Types not found                            | Import from &#x2A;*`@revrag-ai/embed-react-native`*&#x2A;: &#x2A;*`EmbedButtonVisibilityConfig`*&#x2A;, &#x2A;*`EmbedButtonGroupConfig`*&#x2A;, &#x2A;*`EmbedButtonContinuity`*&#x2A;, &#x2A;*`EmbedButtonDelayPolicy`*&#x2A;, &#x2A;*`EmbedButtonInset`**. |

<Accordion title="Quick checks (FAB never shows)">
  1. Log the active route name and compare to &#x2A;*`includeScreens`*&#x2A; / group &#x2A;*`screens`**.
  2. Confirm &#x2A;*`EmbedProvider`** is **outside*&#x2A; &#x2A;*`NavigationContainer`*&#x2A; and &#x2A;*`ref`** is the same object reference.
  3. Temporarily set a broad &#x2A;*`includeScreens`** list to verify routing, then tighten.
</Accordion>

***

## 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*&#x2A; when QA reports "delay only happened once" - often &#x2A;*`oncePerAppSession`*&#x2A; or &#x2A;*`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&#x29; (install, native setup, &#x2A;*`useInitialize`*&#x2A;, &#x2A;*`USER_DATA`**, events).

***

## Related documentation [#related-documentation]

<CardGroup cols="1">
  <Card title="Back to React Native integration" href="/embed/integration/react-native">
    Installation, native LiveKit setup, &#x2A;*`GestureHandlerRootView`*&#x2A;, &#x2A;*`useInitialize`*&#x2A;, &#x2A;*`EmbedProvider`*&#x2A; basics, &#x2A;*`Embed.Event`**, and troubleshooting.
  </Card>
</CardGroup>
