Skip to main content

React and Next.js (@hatang/chat-react)

@hatang/chat-react loads the Ha Tang chat widget in a React or Next.js app. It gives you a component that renders nothing and injects the widget script once, and a hook that returns the typed $hatang JavaScript API. The widget is still the web widget, with the same look and the same dashboard settings; this package only handles loading it and the TypeScript types.

Version

@hatang/chat-react 0.1.2 · React 18+ · Next.js 14+ (App Router and Pages Router) · npm · changelog: CHANGELOG.md in the package

Before you start​

  • A website channel in Ha Tang. Copy its Site ID (site_...) from Support → Channels → your website.
  • The domain your app runs on must be the website's domain (or one of its subdomains), or be listed under Extra allowed origins in the website's settings. Add http://localhost:3000 there for local development.

Install​

npm install @hatang/chat-react

Load the widget​

Render <HatangChat /> once, in your root layout. The module starts with "use client", so you can use it directly in the App Router.

app/layout.tsx
import { HatangChat } from "@hatang/chat-react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<HatangChat siteId="site_123..." />
</body>
</html>
);
}

On first render the component sets window.HATANG_SITE_ID, sets window.HATANG_CONFIG when you pass config, and appends https://chat.hatang.dev/widget.js with async. It never adds the script twice, even when it renders again or more than once. Once the first page loads, the website in the dashboard shows that the widget is installed.

Props​

PropTypeDefaultWhat it does
siteIdstringrequiredThe website's Site ID
hoststringhttps://chat.hatang.devWhere widget.js is served from. Change it only for a local gateway
configHatangConfig—{ position?: "left" | "right"; locale?: "vi" | "en"; hideLauncher?: boolean }, written to window.HATANG_CONFIG before the script loads
userHatangIdentity—{ userId?, email?, name?, phone?, hash?, data? }. Calls identify when the widget is ready, and again whenever userId, email, name or hash changes

config is read once, when the script is first added; later changes to it do not reach the widget. To switch language at runtime, call setLocale. The HATANG_CONFIG options offset and mobileOffset are not in the HatangConfig type yet; if you need them, set window.HATANG_CONFIG yourself before the component renders (see Web widget and JS API).

Call the widget from your code​

useHatang() (a hook) and hatang() (a plain function, for code outside components) return the same object. Calls made before the widget has loaded are queued, so you can call them at any time in the browser. On the server they do nothing.

"use client";
import { useHatang } from "@hatang/chat-react";

export function SupportButton() {
const chat = useHatang();
return <button onClick={() => chat.open()}>Chat with us</button>;
}
MethodWhat it does
open(), close(), toggle()Open, close or toggle the chat window
hide(), show()Hide or show the launcher button (for example on a checkout page)
identify(identity)Attach the signed-in user; hash is computed on your server (Identity verification)
setData(data)Custom attributes shown to agents
setLocale("vi" | "en")Change the widget's language
pushEvent(name, data?)Record an event on the visitor's timeline
on(event, cb), off(event, cb)Listen to ready, open, close, message:received, message:sent, conversation:status
reset()Forget the current visitor (on sign-out). The widget reloads the page to start a fresh session
"use client";
import { useEffect } from "react";
import { hatang } from "@hatang/chat-react";

export function ChatAnalytics() {
useEffect(() => {
const chat = hatang();
const onReply = (m?: unknown) => track("chat_reply", m);
chat.on("message:received", onReply);
return () => chat.off("message:received", onReply);
}, []);
return null;
}

The widget also takes commands this typed API does not list, such as lead. Push them on the queue yourself: window.$hatang.push(["lead", { email, name }]). The full command and event list is in Web widget and JS API.

Identify signed-in users​

Compute hash, the HMAC-SHA256 of the user id with the site's identity secret, on the server and pass it down with the user. Never put the secret in the client bundle.

app/layout.tsx (Server Component)
import { createHmac } from "node:crypto";
import { HatangChat } from "@hatang/chat-react";
import { currentUser } from "@/lib/auth";

export default async function RootLayout({ children }: { children: React.ReactNode }) {
const user = await currentUser();
const identity = user
? {
userId: user.id,
email: user.email,
name: user.name,
hash: createHmac("sha256", process.env.HATANG_IDENTITY_SECRET!).update(user.id).digest("hex"),
}
: undefined;
return (
<html lang="en">
<body>
{children}
<HatangChat siteId="site_123..." user={identity} />
</body>
</html>
);
}

With a valid hash, the inbox marks the visitor as verified and merges the conversation with that user's earlier sessions, including those from your mobile app. Call hatang().reset() when the user signs out so the next person on the same browser does not inherit the identity.

Without useEffect​

For the Pages Router, other frameworks or a plain HTML template, hatangSnippet(siteId, host?, config?) returns the same inline script the dashboard shows, as one string. Put it in a <script> tag or in next/script:

import Script from "next/script";
import { hatangSnippet } from "@hatang/chat-react";

<Script id="hatang" strategy="afterInteractive">
{hatangSnippet("site_123...", undefined, { locale: "en" })}
</Script>

Exports​

HatangChat, HatangChatProps, hatang, useHatang, HatangApi, HatangIdentity, HatangEvent, HatangConfig, hatangSnippet, inlineSnippet and widgetUrl.

Changelog​

0.1.1: the npm page describes the widget and Ha Tang and links this guide; the README ends with an About Ha Tang section. No code changes.

0.1.0: first release. <HatangChat siteId host config user /> loads the widget once; useHatang() / hatang() with open, close, toggle, hide, show, identify, setData, setLocale, pushEvent, on, off and reset, queued until the widget loads.

Next steps​

View this page as Markdown