React (Vite or Create React App)
Add relaybell to a single-page React app built with Vite or Create React App: the snippet in index.html, sw.js in public/, a subscribe button and signed-in users.
A single-page React app loads one HTML page, so the plain Quickstart snippet is all you need: it goes in index.html, and your components talk to window.RelayBell. Your Project ID is on the dashboard's Install page. (Using Next.js? See Next.js.)
1. Add the snippet to index.html
Paste it just before </body> — in Vite that's index.html at the project root, in Create React App public/index.html:
<script src="https://relaybell.com/relaybell-sdk.js"></script>
<script>
RelayBell.init({ projectId: "YOUR_PROJECT_ID", apiBase: "https://relaybell.com" });
</script>The page loads it once, and it stays loaded while your router changes pages, so init() runs once per page load — as it should (a second call starts again from its own options). Don't call init() from a component.
Both tools load your app's own code after these classic scripts have run, so window.RelayBell is there by the time your components mount.
2. Add the service worker
Download sw.js and save it as public/sw.js. Vite and Create React App both serve public/ from the root of your site, so it opens at https://yoursite.com/sw.js — the default swPath.
Note
If your app already registers its own service worker at /sw.js (a PWA plugin, for example), keep it: save relaybell's as public/relaybell/sw.js and add swPath: "/relaybell/sw.js", swScope: "/relaybell/" to init(). See Installing the SDK.
3. TypeScript types
If your app uses TypeScript, declare what you use from window.RelayBell, for example in src/relaybell.d.ts:
export {};
declare global {
interface Window {
RelayBell?: {
subscribeNow(): Promise<{ supported: boolean; subscribed: boolean; permission?: NotificationPermission; reason?: string }>;
isSubscribed(): Promise<boolean>;
identify(externalId: string | null, userHash?: string): Promise<void>;
unsubscribe(): Promise<{ unsubscribed: boolean }>;
setTags(tags: Record<string, string | number | boolean>): Promise<Record<string, unknown>>;
track(event: string, properties?: Record<string, unknown>): Promise<unknown>;
};
}
}4. A subscribe button (optional)
relaybell shows your opt-in prompt by itself (Opt-in prompts). For your own button, call subscribeNow() from the click — browsers only show the permission prompt in answer to one:
import { useEffect, useState } from "react";
export function NotifyButton() {
const [on, setOn] = useState(false);
useEffect(() => {
window.RelayBell?.isSubscribed().then(setOn);
}, []);
if (on) return <p>Notifications are on.</p>;
return (
<button
onClick={async () => {
const result = await window.RelayBell?.subscribeNow();
if (result?.subscribed) setOn(true);
else if (result && !result.supported) alert("This browser can't show notifications from this site.");
}}
>
Turn on notifications
</button>
);
}On an iPhone or iPad, supported is false until the visitor adds your site to their Home Screen — see Browser support.
5. Link signed-in users
To send to one of your users by your own user id, call identify() once you know who is signed in. With identity verification, pass the userHash your server made for that user (the HMAC-SHA256 of the id with your website's secret key) — make it on the server and send it to the app with the signed-in user's details; never put the secret key in the app.
import { useEffect } from "react";
// user = the signed-in user from your own auth code, with userHash added by your server
export function useRelayBellUser(user: { id: string; relaybellHash: string } | null) {
const id = user?.id;
const hash = user?.relaybellHash;
useEffect(() => {
if (id) window.RelayBell?.identify(id, hash);
}, [id, hash]);
}Calling identify() again with the same values does nothing, so it's fine in an effect. On a shared browser, call window.RelayBell?.unsubscribe() when someone signs out of a site without identity verification — see identify() under Methods.
6. Track events
track() feeds your automations (abandoned cart, product alerts, journeys) and conversion reports:
window.RelayBell?.track("cart.updated", { productId: "1234", product: "Blue Shoes", price: 49, url: location.href });
window.RelayBell?.track("purchase", { value: 79.99, orderId: "A123" });Or add autoCapture: true to init(): it records page views — including your router's page changes — and clicks on elements with data-rb-event. See Auto-capture.