Install

Next.js

Install Whazzup in the app router with server-side identity.

Root loader

Use the Script component in the root layout. Always sign user identities on the server.

app/layout.tsx
import Script from "next/script";

<Script
  src="https://whazzup.io/widget.js"
  data-workspace="YOUR_WORKSPACE_KEY"
  strategy="afterInteractive"
/>

Identity route

1. Install with user data — app/WhazzupWidget.tsx

One component publishes the signed-in person and loads the widget. Call logout explicitly when the person signs out.

"use client";
import Script from "next/script";
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]);

  return <Script src="https://whazzup.io/widget.js" data-workspace="YOUR_WORKSPACE_KEY" strategy="afterInteractive" />;
}

2. Verified identity (recommended) — app/api/whazzup-identity/route.ts

Keep WHAZZUP_IDENTITY_SECRET in your server environment. Return a fresh, single-use token for the current signed-in user. Tokens expire after 5 minutes — call this endpoint on every full page load and whenever the token may have expired, never cache it.

import { createHmac, randomUUID } from "node:crypto";
import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";

export async function GET() {
  const session = await auth();
  if (!session?.user?.id) return new NextResponse("Unauthorized", { status: 401 });

  const payload = {
    aud: "YOUR_WORKSPACE_KEY",
    sub: session.user.id,
    exp: Math.floor(Date.now() / 1000) + 300,
    jti: randomUUID(),
    name: session.user.name,
    email: session.user.email,
    attributes: {}
  };
  const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
  const signature = createHmac("sha256", process.env.WHAZZUP_IDENTITY_SECRET!)
    .update(body).digest("base64url");
  return NextResponse.json({ token: body + "." + signature });
}

3. Browser — pass the signed token

Load the widget once, then identify after login. Without the token the person stays marked as unverified, even though name and email arrive. In a multi-page app, fetch and pass a fresh token on every page load; in a SPA, refetch after the token expires (5 minutes). Detach identity without destroying the session on sign-out.

"use client";
import Script from "next/script";
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(() => {
    if (!signedIn) { window.whazzup?.("logout"); return; }
    if (window.whazzup) void identify();
  }, [signedIn]);

  return <Script src="https://whazzup.io/widget.js" data-workspace="YOUR_WORKSPACE_KEY" strategy="afterInteractive" onLoad={() => { if (signedIn) void identify(); }} />;
}

Client lifecycle

The client component fetches a token only after authentication and calls logout on sign-out. Keep the signing secret in the server environment.