Skip to main content
The Sendrealm React Web Push SDK registers browser Push API subscriptions with Sendrealm, keeps subscription state synced, and exposes React hooks for permission, identity, tags, diagnostics, and notification events. This SDK runs in the browser and does not use a Sendrealm API key. It uses your Sendrealm Push App ID and the Web Push VAPID public key returned by the SDK API.
Never put a Sendrealm API key in browser JavaScript. Use @sendrealm/react for browser device registration and @sendrealm/sdk from a trusted backend to send notifications.

Requirements

  • React 18 or newer.
  • A Sendrealm Push App with the Web provider active.
  • HTTPS in production. Browser vendors allow localhost for development.
  • The Sendrealm service worker served from the same origin as your app.
  • A user-initiated permission prompt, usually from an Enable notifications button.
iOS Web Push only works for installed Home Screen web apps. Your site must be served over HTTPS, include a valid web app manifest, be added to the Home Screen, and request notification permission from a user gesture inside the installed PWA.

Install

npm install @sendrealm/react
Copy the service worker into your public root:
cp node_modules/@sendrealm/react/sendrealm-service-worker.js public/sendrealm-service-worker.js
Web Push service workers must be served from the same origin as the page. The default SDK path is /sendrealm-service-worker.js with scope /.

Initialize In React Or Vite

The preferred React integration is a client-side init() call. No provider is required.
import { useEffect } from "react";
import { init, useSendrealmSubscription } from "@sendrealm/react";

const sendrealmAppId = "YOUR_SENDREALM_PUSH_APP_ID";

function NotificationButton() {
  const { subscribed, optIn, optOut } = useSendrealmSubscription();

  return (
    <button onClick={() => (subscribed ? optOut() : optIn())}>
      {subscribed ? "Disable notifications" : "Enable notifications"}
    </button>
  );
}

export function App() {
  useEffect(() => {
    void init({
      appId: sendrealmAppId,
      autoRequestPermission: false,
    });
  }, []);

  return <NotificationButton />;
}
init() is idempotent. React StrictMode can run effects twice during development; the SDK still sends only one initialization request for the default singleton client.

Initialize In Next.js

Importing the package is SSR-safe, but initialization needs browser APIs. Put init() in a Client Component.
// app/sendrealm-init.tsx
"use client";

import { useEffect } from "react";
import { init } from "@sendrealm/react";

export function SendrealmInit() {
  useEffect(() => {
    void init({
      appId: process.env.NEXT_PUBLIC_SENDREALM_PUSH_APP_ID!,
      autoRequestPermission: false,
    });
  }, []);

  return null;
}
Render it once near your app shell:
// app/layout.tsx
import { SendrealmInit } from "./sendrealm-init";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <SendrealmInit />
        {children}
      </body>
    </html>
  );
}
Put sendrealm-service-worker.js in public/ so Next serves it at /sendrealm-service-worker.js.

Initialization Options

init({
  appId: "YOUR_SENDREALM_PUSH_APP_ID",
  baseUrl: "https://sdk-api.sendrealm.com",
  environment: "production",
  autoRequestPermission: false,
  serviceWorkerPath: "/sendrealm-service-worker.js",
  serviceWorkerScope: "/",
  externalUserId: "user_123",
  userEmail: "user@example.com",
});
OptionDefaultDescription
appIdRequiredSendrealm Push App ID from the dashboard.
baseUrlhttps://sdk-api.sendrealm.comSDK API base URL. Use this for local development or self-hosted environments.
environmentproductionDevice environment. Use development for test builds if your sends target development devices separately.
autoRequestPermissionfalseIf true, requests notification permission during initialization. Keep false unless initialization happens from a user gesture.
serviceWorkerPath/sendrealm-service-worker.jsSame-origin URL for the copied service worker file.
serviceWorkerScope/Service worker scope. Use / unless your app intentionally isolates push to a subpath.
deviceIdGenerated and storedOptional stable Sendrealm web device ID. Usually let the SDK manage this.
externalUserIdnullOptional user ID to link during initialization. You can also call login after sign-in.
userEmailnullOptional email to link during initialization.

Permission And Subscription UI

Browsers expect notification prompts to happen after a user action. A common pattern is to show your own explanation first, then call optIn() from a button click.
import { useSendrealmPermission, useSendrealmSubscription } from "@sendrealm/react";

export function PushSettings() {
  const { permissionStatus, requestPermission } = useSendrealmPermission();
  const { subscribed, optIn, optOut, refreshRegistrationToken } =
    useSendrealmSubscription();

  return (
    <section>
      <p>Permission: {permissionStatus}</p>
      <p>Subscribed: {subscribed ? "yes" : "no"}</p>
      <button onClick={() => void requestPermission()}>Ask permission</button>
      <button onClick={() => void optIn()}>Enable notifications</button>
      <button onClick={() => void optOut()}>Disable notifications</button>
      <button onClick={() => void refreshRegistrationToken(true)}>
        Refresh subscription
      </button>
    </section>
  );
}
optIn() requests permission if needed, subscribes through PushManager.subscribe, and registers the full PushSubscription.toJSON() payload with Sendrealm. optOut() unsubscribes the browser subscription and marks the Sendrealm device unsubscribed.

Identity, Tags, And Events

Link the browser device after the user signs in:
import { getSendrealmClient } from "@sendrealm/react";

const sendrealm = getSendrealmClient();

await sendrealm.login("user_123", "user@example.com");
Remove the identity on sign-out:
await sendrealm.logout();
Add client-observed tags:
await sendrealm.addTags({
  plan: "pro",
  onboarding_complete: true,
  locale: "en-US",
});
Track custom events:
await sendrealm.trackEvent("checkout.started", {
  cart_id: "cart_123",
  total: 42,
});
Use tags for client-known state such as preferences, locale, feature flags, or onboarding progress. Use server-side contact properties for authoritative account, billing, compliance, and CRM data.

Notification Events

Register listeners after init() or in a React effect that runs with the SDK client.
import { useEffect } from "react";
import { useSendrealm } from "@sendrealm/react";

export function NotificationEvents() {
  const { client } = useSendrealm();

  useEffect(() => {
    const opened = client.addNotificationClickListener(event => {
      console.log("opened", event.launchUrl, event.notificationId);
    });
    const action = client.addNotificationActionListener(event => {
      console.log("action", event.actionIdentifier);
    });
    const foreground = client.addForegroundNotificationListener(event => {
      console.log("foreground", event.payload);
    });
    const silent = client.addSilentNotificationListener(event => {
      console.log("silent", event.payload);
    });

    return () => {
      opened.remove();
      action.remove();
      foreground.remove();
      silent.remove();
    };
  }, [client]);

  return null;
}
The service worker also stores the last notification open so cold-start flows can read it:
const initialOpen = await getSendrealmClient().getInitialNotification();

Automatic Event Tracking

The browser SDK and service worker track notification lifecycle events when the SDK has initialized and the notification payload contains Sendrealm metadata.
EventWhen it is tracked
deliveryThe service worker receives a Sendrealm notification payload.
foreground_displayA notification is received while a visible client exists.
background_notification_receivedA notification is received while no visible client exists.
openThe user opens the notification.
clickThe user clicks the notification body or an action.
notification_actionThe user clicks a notification action button.
dismissThe browser fires notificationclose.
push_subscription_changedThe browser fires pushsubscriptionchange.
permission_granted / permission_deniedThe page observes a notification permission status change.
Browsers control which service worker events fire. For example, some browsers do not fire notificationclose for every dismissal.

Launch URLs

When the user opens a notification, the service worker resolves the launch URL in this order:
  1. The clicked action’s launch_url or launchUrl.
  2. Notification metadata web_launch_url.
  3. Notification metadata launch_url.
  4. Notification data.launch_url.
  5. /.
Then it calls clients.openWindow(launchUrl) and notifies active page clients with SENDREALM_NOTIFICATION_CLICKED. In app code, listen with addNotificationClickListener or addNotificationActionListener. Send a launch URL from the public API:
await client.push.notifications.send({
  app_id: "push_app_short_id",
  external_ids: ["user_123"],
  platforms: ["web"],
  notification: {
    title: "Order update",
    body: "Your order is ready.",
    launch_url: "https://app.example.com/orders/123",
  },
});

Rich Images And Actions

Sendrealm forwards notification images to the browser showNotification({ image }) option when the browser supports rich web notification images. Browsers that do not support image rendering still show the title, body, icon, and badge. Supported image fields include:
  • notification.image
  • notification.imageUrl
  • notification.image_url
  • notification.web.image
  • notification.web.imageUrl
  • notification.web.image_url
  • metadata.image_url
  • metadata.imageUrl
From the public API, use notification.image_url:
await client.push.notifications.send({
  app_id: "push_app_short_id",
  audiences: ["audience_123"],
  platforms: ["web"],
  notification: {
    title: "New collection",
    body: "See what just launched.",
    image_url: "https://cdn.example.com/collection.png",
    launch_url: "https://app.example.com/collections/new",
  },
  buttons: [
    {
      id: "view",
      text: "View",
      launch_url: "https://app.example.com/collections/new",
    },
  ],
});
Web notifications render up to two action buttons because that is the browser notification API limit used by the service worker. Browser UI, image size, icons, action buttons, and grouping behavior vary by browser and operating system.

Send To Web Devices

Once the SDK registers a browser, you can target web devices by:
  • device_ids
  • contact_ids
  • external_ids
  • emails
  • audiences
  • platforms: ["web"]
You can also send directly to raw browser subscriptions with web_subscriptions from a trusted backend. Do not collect raw browser subscriptions manually unless you have a specific direct-send use case; SDK-registered devices are easier to target and manage.
await client.push.notifications.send({
  app_id: "push_app_short_id",
  external_ids: ["user_123"],
  platforms: ["web"],
  notification: {
    title: "Hello from Sendrealm",
    body: "This targets the user's registered web browsers.",
  },
});

Diagnostics

Use diagnostics to confirm browser support, device ID, permission status, subscription state, service worker path, and the last SDK error.
const diagnostics = await getSendrealmClient().getDiagnostics();
console.log(diagnostics);
For support flows, collect the redacted diagnostics payload:
const diagnostics = await getSendrealmClient().getSupportDiagnostics();

Hooks

HookReturns
useSendrealm(){ client, state, initializing, error } for the default singleton client, or the nearest provider client if you use SendrealmProvider.
useSendrealmPermission(){ permissionStatus, permissionGranted, requestPermission }.
useSendrealmSubscription(){ subscribed, token, optIn, optOut, refreshRegistrationToken }.
The hooks subscribe to the default singleton client after init(). SendrealmProvider remains available for advanced apps that need to inject a custom SendrealmWebClient, but it is not required.

Client API

MethodDescription
init(options) / initialize(options)Initializes the default singleton client.
getSendrealmClient()Returns the default singleton client.
Sendrealm.initialize(options)Initializes the exported singleton directly.
login(userId, email?)Links the web device to an external user ID and optional email.
logout()Clears the linked identity from the device.
requestPermission()Shows the browser notification permission prompt.
hasNotificationPermission()Returns whether permission is currently authorized.
getPermissionStatus()Returns authorized, denied, not_determined, unsupported, or a browser-specific status.
getDeviceId()Returns the Sendrealm web device ID.
isSubscribed()Returns whether the SDK currently has a registered browser subscription.
getState()Returns the SDK state snapshot.
getDiagnostics()Returns SDK diagnostics.
getSupportDiagnostics()Returns diagnostics suitable for support collection.
refreshRegistrationToken(forceRefresh?)Refreshes the browser PushSubscription and registers it.
optIn()Enables web push for this browser.
optOut()Disables web push for this browser.
addTag(key, value) / addTags(tags)Writes SDK tags for this device/contact.
removeTag(key)Removes an SDK tag.
trackEvent(event, properties?)Tracks a custom event from the browser.
getInitialNotification()Reads the notification open that launched the page, when available.
addNotificationClickListener(listener)Observes notification body opens.
addNotificationActionListener(listener)Observes action button clicks.
addForegroundNotificationListener(listener)Observes received push payloads while a page client is visible.
addSilentNotificationListener(listener)Observes silent/background-style payloads delivered to an active page client.
addPermissionObserver(listener)Observes notification permission changes.
addSubscriptionObserver(listener)Observes subscription token changes.
Mobile-only APIs such as Android notification channels, APNs token injection, and Live Activities are unsupported or no-ops on web.

Troubleshooting

Sendrealm Web Push requires window, Notification, serviceWorker, and PushManager

init() ran somewhere without browser push APIs. In Next.js, move initialization to a Client Component and call it from useEffect.

Sendrealm Web Push public key is unavailable

The Web provider is not active or the SDK API did not return web_push.public_key. Activate the Web provider for the Push App in the dashboard and try again.

Registration failed - push service error

The browser exposed PushManager but could not create a subscription with its push service. This is common in embedded Chromium browsers, browsers with push messaging disabled, and local setups using a numeric loopback host. Try:
  • Open the app in a full Chrome, Edge, Brave, or Safari browser.
  • Use http://localhost:PORT instead of http://127.0.0.1:PORT.
  • Clear the origin’s site data and service worker registrations.
  • Confirm the browser can reach its push service.
  • Confirm notification permission is not blocked at the browser or OS level.

Notifications do not open the expected URL

Confirm the send payload includes notification.launch_url or an action-level launch_url. The browser must also allow the service worker to open the target URL.

Images do not appear

Confirm the image URL is HTTPS, publicly reachable by the browser, and sent as notification.image_url. Some browsers ignore rich notification images even when the payload is valid.

Local Demo

The package includes a demo app under sdks/react/demo.
cd sdks/react
pnpm install
VITE_SENDREALM_APP_ID=YOUR_PUSH_APP_ID pnpm demo
Open the demo through http://localhost:5174 rather than a numeric loopback address.