ReachBellDocs

React integration

The @reachbell/react package wraps the core SDK in a provider and a pair of hooks so your components can read the current permission state, prompt the user, and identify or tag the subscriber without touching the global ReachBell object directly.

Install

npm install @reachbell/react @reachbell/sdk

@reachbell/react re-exports the SDK from @reachbell/sdk for direct access when you need it, but day to day you should rarely reach past the hooks.

Wrap your tree

Mount <ReachBellProvider> once at the top of your React tree — typically inside whichever component renders your application root.

import { ReachBellProvider } from '@reachbell/react';

export default function App({ children }) {
  return (
    <ReachBellProvider
      apiKey="rb_live_yourkey"
      autoPrompt
    >
      {children}
    </ReachBellProvider>
  );
}

Provider props

  • apiKey — required. Your project's API key.
  • apiUrl — optional. Override the API base URL for self-hosted or regional deployments.
  • autoPrompt — optional boolean. Arms a one-shot user-gesture listener that triggers the soft prompt on the first interaction.

StrictMode-safe. The provider holds a useRef guard around ReachBell.init() so React 18+ StrictMode's double-invoke dev pass does not re-init the SDK. The SDK itself is also idempotent on init, so the worst case in any edge condition is a no-op, never a duplicate service worker registration.

Drop the service worker at the root

@reachbell/react is a thin wrapper, not a replacement for the service worker file. You still need reachbell-sw.js served from your site's root:

https://yoursite.com/reachbell-sw.js

If your bundler outputs to dist/ or build/, copy the file into the static asset directory that ends up at the deployed root. For Next.js, see the dedicated Next.js guide.

useReachBell()

The primary hook. Returns the SDK's reactive state and a stable set of action callbacks.

import { useReachBell } from '@reachbell/react';

function SubscribeButton() {
  const { isReady, permission, prompt } = useReachBell();

  if (!isReady) return null;
  if (permission === 'granted') return <span>You're subscribed.</span>;
  if (permission === 'denied') return <span>Notifications blocked.</span>;

  return (
    <button
      onClick={async () => {
        const result = await prompt();
        if (!result.ok) console.warn('prompt failed:', result.reason);
      }}
    >
      Get notified
    </button>
  );
}

Returned shape

  • isReadyboolean. Becomes true once ReachBell.init() has resolved successfully.
  • permission'default' | 'granted' | 'denied'. Mirrors Notification.permission and re-renders when it changes.
  • prompt() — wraps ReachBell.prompt(). Returns { ok, reason }.
  • identify(externalId) — wraps ReachBell.identify(). Returns { ok, reason }.
  • tag(tags) — wraps ReachBell.tag(). Pass a string array. Returns { ok, reason }.
  • sdk — the underlying ReachBell instance. Escape hatch for anything the hook doesn't expose.

useNotificationPermission()

A lighter hook that only tracks the browser's permission state. Useful when a component cares about the permission but doesn't need to call any actions.

import { useNotificationPermission } from '@reachbell/react';

function PermissionBadge() {
  const { permission, refresh } = useNotificationPermission();

  return (
    <div>
      <span>Status: {permission}</span>
      <button onClick={refresh}>Re-check</button>
    </div>
  );
}

refresh() re-reads Notification.permission synchronously. The hook also listens for the permissionchange event under the hood, so most apps never need to call refresh() manually — it's there for the rare case where the browser's event doesn't fire (some Safari versions).

Identify after login

The cleanest place to call identify() is in the effect that runs when your auth state transitions from anonymous to logged-in.

import { useEffect } from 'react';
import { useReachBell } from '@reachbell/react';
import { useCurrentUser } from './auth';

function IdentifyOnLogin() {
  const { isReady, identify } = useReachBell();
  const user = useCurrentUser();

  useEffect(() => {
    if (!isReady || !user) return;

    identify(user.id).then((r) => {
      if (!r.ok) console.warn('identify failed:', r.reason);
    });
  }, [isReady, user, identify]);

  return null;
}

Render it once anywhere inside the provider — it has no UI of its own, it's just a side effect.

Tag based on plan

Same pattern for tags. The example below replaces the device's tag set whenever the user's plan changes.

function TagOnPlanChange() {
  const { isReady, tag } = useReachBell();
  const { plan } = useCurrentUser() ?? {};

  useEffect(() => {
    if (!isReady || !plan) return;
    tag([plan]);
  }, [isReady, plan, tag]);

  return null;
}

Tag replaces, it doesn't append. Each tag() call overwrites the device's tag set. If you want additive semantics, read the current tags from the dashboard or your own user store and merge before calling.

What's next?

  • If you're on Next.js App Router, read the Next.js guide for the "use client" provider pattern.
  • See the Subscribers API for the server-side counterpart to identify() and tag().
  • Build a re-engagement series with Automation.