Skip to main content

EmbedProvider — Basic to Advanced Guide for React

EmbedProvider is a React context provider that manages where and when the AI widget appears across your app. Instead of manually placing <EmbedButton /> on every page, you wrap your app once and let the provider handle everything.

How It Works (Internals Overview)

EmbedProvider
 ├── Detects current route (via usePathHook → currentPath prop → window.location fallback)
 ├── Checks if the route is in includeScreens
 ├── Applies delay logic (embedButtonDelayMs / group config)
 └── Renders <EmbedButton /> when conditions are met
  • If includeScreens is empty → widget shows on every route
  • If includeScreens has values → widget shows only on those routes
  • Path detection priority: currentPath prop > usePathHook > window.location

Level 1 — Show Widget on Every Page

The simplest setup. No route filtering, no delays.
// app/layout.tsx (Next.js) or your root component
"use client";

import { useInitialize, EmbedProvider } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/dist/ai-assistant-widget.css';

export default function RootLayout({ children }) {
  useInitialize("your-api-key");

  return (
    <html lang="en">
      <body>
        <EmbedProvider>
          {children}
        </EmbedProvider>
      </body>
    </html>
  );
}
The widget appears on every route immediately. That’s it.

Level 2 — Show Widget Only on Specific Routes

Use includeScreens to whitelist which routes show the widget.

Exact Match (default)

/help matches only /help — not /help/faq.
<EmbedProvider
  usePathHook={useNextPathname}
  includeScreens={['/help', '/support', '/contact']}
  matchMode="exact"
>
  {children}
</EmbedProvider>

Prefix Match

/help matches /help, /help/faq, /help/contact, etc.
<EmbedProvider
  usePathHook={useNextPathname}
  includeScreens={['/help', '/dashboard']}
  matchMode="startsWith"
>
  {children}
</EmbedProvider>

Injecting the Router Hook

The provider needs to know the current path. Pass your router’s hook so it updates on navigation. Next.js App Router:
import { usePathname } from 'next/navigation';

// Wrap in a named function — required because hooks must be
// called unconditionally and stably inside the provider
function useNextPathname() {
  return usePathname();
}

<EmbedProvider usePathHook={useNextPathname} includeScreens={['/help']} />
React Router:
import { useLocation } from 'react-router-dom';

function useReactRouterPath() {
  return useLocation().pathname;
}

<EmbedProvider usePathHook={useReactRouterPath} includeScreens={['/help']} />
Manual override (any framework):
// Pass the path directly — highest priority, overrides everything
<EmbedProvider currentPath={myCurrentPath} includeScreens={['/help']} />

Level 3 — Delay the Widget Appearance

Show the widget after the user has been on a screen for a few seconds, so it doesn’t feel intrusive.
<EmbedProvider
  usePathHook={useNextPathname}
  includeScreens={['/pricing', '/checkout']}
  embedButtonDelayMs={4000}   // wait 4 seconds after arriving on the screen
>
  {children}
</EmbedProvider>
What happens:
  1. User navigates to /pricing
  2. Provider hides the widget and starts a 4-second timer
  3. Timer fires → widget appears with animation
  4. User navigates away → widget hides immediately, timer resets
  5. User comes back to /pricing → 4-second timer starts again

Level 4 — Customize the Button Position

Override where the floating button sits on the screen.
<EmbedProvider
  usePathHook={useNextPathname}
  embedButtonPosition={{ bottom: 80, right: 24 }}  // numbers = px
>
  {children}
</EmbedProvider>
You can also pass CSS strings:
embedButtonPosition={{ bottom: '5rem', right: '1.5rem' }}
Pass any EmbedButton prop through embedButtonProps:
<EmbedProvider
  usePathHook={useNextPathname}
  embedButtonProps={{
    positioning: 'fixed',
    bottomOffset: 60,       // offset for a 60px bottom navbar
    className: 'my-widget',
  }}
>
  {children}
</EmbedProvider>

Level 5 — Group-Based Visibility

This is the advanced visibility engine. Use it when different sections of your app need different delay or continuity behavior.

The Problem It Solves

Without groups, every navigation triggers the delay timer — so if a user moves between /checkout and /payment (both part of checkout), the widget keeps hiding and re-appearing. Groups prevent that.

Core Concepts

continuity — controls what happens when navigating within a group:
ValueBehavior
"continuous"Widget stays visible — no re-animation when moving between screens in the same group
"perScreen"Widget re-applies delay on every screen, even within the group
delayPolicy — controls when the delay fires:
ValueBehavior
"perScreen"Delay fires on every screen in the group
"oncePerGroupEntry"Delay fires only the first time the user enters this group
"oncePerAppSession"Delay fires at most once per browser session for this group

Basic Groups Example

import { EmbedProvider } from '@revrag-ai/embed-react';
import type { EmbedButtonVisibilityConfig } from '@revrag-ai/embed-react';

const visibilityConfig: EmbedButtonVisibilityConfig = {
  groups: [
    {
      id: 'checkout-flow',
      screens: ['/cart', '/checkout', '/payment', '/confirmation'],
      continuity: 'continuous',         // no re-animation between checkout steps
      delayMs: 2000,
      delayPolicy: 'oncePerGroupEntry', // delay only on first entry to checkout
    },
  ],
};

<EmbedProvider
  usePathHook={useNextPathname}
  embedButtonVisibilityConfig={visibilityConfig}
>
  {children}
</EmbedProvider>
What happens:
  1. User is on /home → widget hidden (not in any group)
  2. User goes to /cart → 2-second delay, then widget appears
  3. User goes to /checkout → widget stays visible (same group, continuous)
  4. User goes to /payment → widget stays visible (same group, continuous)
  5. User leaves to /home → widget hidden
  6. User comes back to /cartno delay this time (oncePerGroupEntry — already triggered)

Multiple Groups

const visibilityConfig: EmbedButtonVisibilityConfig = {
  defaultDelayMs: 1000,   // fallback delay for screens not in any group

  groups: [
    {
      id: 'onboarding',
      screens: ['/welcome', '/setup', '/profile-setup'],
      continuity: 'continuous',
      delayMs: 5000,
      delayPolicy: 'oncePerAppSession', // only delays once per browser session
    },
    {
      id: 'checkout',
      screens: ['/cart', '/checkout', '/payment'],
      continuity: 'continuous',
      delayMs: 2000,
      delayPolicy: 'oncePerGroupEntry',
    },
    {
      id: 'support',
      screens: ['/help', '/faq', '/contact'],
      continuity: 'perScreen',          // re-animate on every support page
      delayMs: 3000,
      delayPolicy: 'perScreen',
    },
  ],
};

defaultDelayMs

Applies to any screen that is included (via includeScreens) but not in any group:
<EmbedProvider
  usePathHook={useNextPathname}
  includeScreens={['/dashboard', '/settings', '/help']}
  embedButtonVisibilityConfig={{
    defaultDelayMs: 2000,   // applies to /dashboard and /settings
    groups: [
      {
        id: 'support',
        screens: ['/help'],   // /help gets its own group config
        continuity: 'perScreen',
        delayMs: 5000,
        delayPolicy: 'perScreen',
      },
    ],
  }}
>
  {children}
</EmbedProvider>

Level 6 — Reading Current Path in Children

Any component inside EmbedProvider can access the current path via useEmbed:
import { useEmbed } from '@revrag-ai/embed-react';

function Breadcrumb() {
  const { currentPath } = useEmbed();

  return <nav>You are at: {currentPath}</nav>;
}
useEmbed() throws if called outside EmbedProvider. Always use it inside the provider tree.

Complete Real-World Example

A Next.js app with multiple sections, each with their own widget behavior:
// app/layout.tsx
"use client";

import { usePathname } from 'next/navigation';
import { useInitialize, EmbedProvider } from '@revrag-ai/embed-react';
import '@revrag-ai/embed-react/dist/ai-assistant-widget.css';
import type { EmbedButtonVisibilityConfig } from '@revrag-ai/embed-react';

function useNextPathname() {
  return usePathname();
}

const visibilityConfig: EmbedButtonVisibilityConfig = {
  defaultDelayMs: 1500,
  groups: [
    {
      // Onboarding: delay once per session, stay visible through all steps
      id: 'onboarding',
      screens: ['/welcome', '/setup', '/verify'],
      continuity: 'continuous',
      delayMs: 6000,
      delayPolicy: 'oncePerAppSession',
    },
    {
      // Checkout: delay once per entry, no re-animation between steps
      id: 'checkout',
      screens: ['/cart', '/checkout', '/payment', '/order-confirmed'],
      continuity: 'continuous',
      delayMs: 3000,
      delayPolicy: 'oncePerGroupEntry',
    },
    {
      // Support: always delay, re-animate on each page (high intent section)
      id: 'support',
      screens: ['/help', '/faq', '/contact'],
      continuity: 'perScreen',
      delayMs: 2000,
      delayPolicy: 'perScreen',
    },
  ],
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const { error } = useInitialize("your-api-key");

  if (error) console.error('[EmbedSDK] Init error:', error);

  return (
    <html lang="en">
      <body>
        <EmbedProvider
          usePathHook={useNextPathname}
          matchMode="startsWith"
          embedButtonVisibilityConfig={visibilityConfig}
          embedButtonProps={{ positioning: 'fixed', bottomOffset: 0 }}
          embedButtonPosition={{ bottom: 24, right: 24 }}
        >
          {children}
        </EmbedProvider>
      </body>
    </html>
  );
}

Props Quick Reference

PropTypeDefaultPurpose
childrenReactNodeYour app content
currentPathstringManual path override (highest priority)
usePathHook() => stringRouter hook for automatic path detection
includeScreensstring[][] (all)Routes where the widget appears
matchMode"exact" | "startsWith""exact"How routes are matched
embedButtonDelayMsnumber0Global delay before widget appears (ms)
embedButtonVisibilityConfigEmbedButtonVisibilityConfigAdvanced group-based visibility
embedButtonPropsEmbedButtonPropsProps forwarded to <EmbedButton />
embedButtonPosition{ bottom?, right? }{ bottom: 20, right: 16 }Fixed position of the floating button

Common Mistakes

Passing the hook result instead of the hook itself:
// ❌ Wrong — passes the path string, not the hook
<EmbedProvider usePathHook={usePathname()} />

// ✅ Correct — passes the hook function
function useNextPathname() { return usePathname(); }
<EmbedProvider usePathHook={useNextPathname} />
Using useEmbed outside the provider:
// ❌ Throws an error
function ComponentOutsideProvider() {
  const { currentPath } = useEmbed(); // Error!
}

// ✅ Must be inside EmbedProvider tree
function ComponentInsideProvider() {
  const { currentPath } = useEmbed(); // Works
}
Expecting includeScreens + groups to be separate: Screens listed in groups[].screens are automatically added to the include list — you don’t need to repeat them in includeScreens.
// ✅ You don't need to list /help in includeScreens — it's already in the group
<EmbedProvider
  embedButtonVisibilityConfig={{
    groups: [{ id: 'support', screens: ['/help'], continuity: 'perScreen' }]
  }}
/>
Forgetting "use client" in Next.js App Router:
// ✅ Required when using EmbedProvider in Next.js App Router
"use client";

import { EmbedProvider } from '@revrag-ai/embed-react';