Install
React
Mount the loader once and connect signed-in users.
Anonymous install
Load the script in a component that renders once. Call update after every navigation.
src/App.tsx or root layout
// Add once in your root component
useEffect(() => {
const script = document.createElement("script");
script.src = "https://whazzup.io/widget.js";
script.dataset.workspace = "YOUR_WORKSPACE_KEY";
script.async = true;
document.body.appendChild(script);
return () => script.remove();
}, []);Authenticated install
1. Install with user data — WhazzupWidget.tsx
Mount once near your application root. The widget reads account changes automatically; logout is explicit.
import { useEffect } from "react";
type User = { id: string; name: string; email: string } | null;
export function WhazzupWidget({ user }: { user: User }) {
useEffect(() => {
window.whazzupSettings = user
? { name: user.name, email: user.email, userId: user.id }
: null;
if (!user) window.whazzup?.("logout");
}, [user]);
useEffect(() => {
const script = document.createElement("script");
script.src = "https://whazzup.io/widget.js";
script.dataset.workspace = "YOUR_WORKSPACE_KEY";
script.async = true;
document.body.appendChild(script);
return () => script.remove();
}, []);
return null;
}2. Verified identity (recommended) — GET /api/whazzup-identity
Add this logic to your existing authenticated server endpoint. Never send the secret to React.
// Server only — never expose WHAZZUP_IDENTITY_SECRET in the browser
import { createHmac } from 'node:crypto';
const payload = {
aud: 'YOUR_WORKSPACE_KEY',
sub: user.id,
exp: Math.floor(Date.now() / 1000) + 300,
jti: crypto.randomUUID(),
name: user.name,
email: user.email,
attributes: { plan: user.plan }
};
const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
const signature = createHmac('sha256', process.env.WHAZZUP_IDENTITY_SECRET)
.update(body).digest('base64url');
return body + '.' + signature;3. Browser — pass the signed token
Mount once near your application root. The endpoint must return { token } for the current signed-in user. Fetch a fresh token on every full page load and after expiry (5 minutes, single-use) — otherwise people stay marked as unverified.
import { useEffect } from "react";
declare global { interface Window { whazzup: (...args: unknown[]) => void } }
export function WhazzupWidget({ signedIn }: { signedIn: boolean }) {
const identify = () => fetch("/api/whazzup-identity")
.then((response) => response.json())
.then(({ token }) => window.whazzup("identify", { token }));
useEffect(() => {
const script = document.createElement("script");
script.src = "https://whazzup.io/widget.js";
script.dataset.workspace = "YOUR_WORKSPACE_KEY";
script.async = true;
script.onload = () => { if (signedIn) void identify(); };
document.body.appendChild(script);
return () => { script.remove(); };
}, []);
useEffect(() => {
if (!signedIn) { window.whazzup?.("logout"); return; }
if (window.whazzup) void identify();
}, [signedIn]);
return null;
}Lifecycle
Mount the loader once. Identify after login, use update after meaningful navigation changes, and call shutdown before another account uses the same browser tab.
