# 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](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md). 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.

> **Info: Version**
>
> `@hatang/chat-react` **0.1.2** · React 18+ · Next.js 14+ (App Router and Pages Router) · [npm](https://www.npmjs.com/package/@hatang/chat-react) · 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

```bash
npm install @hatang/chat-react
```

## Load the widget {#load}

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

```tsx title="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 {#props}

| Prop | Type | Default | What it does |
|---|---|---|---|
| `siteId` | `string` | required | The website's Site ID |
| `host` | `string` | `https://chat.hatang.dev` | Where `widget.js` is served from. Change it only for a local gateway |
| `config` | `HatangConfig` | — | `{ position?: "left" \| "right"; locale?: "vi" \| "en"; hideLauncher?: boolean }`, written to `window.HATANG_CONFIG` before the script loads |
| `user` | `HatangIdentity` | — | `{ 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](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md)).

## Call the widget from your code {#api}

`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.

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

export function SupportButton() {
  const chat = useHatang();
  return <button onClick={() => chat.open()}>Chat with us</button>;
}
```

| Method | What 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](https://hatang.dev/docs/guides/support/web-widget/identity-verification.md)) |
| `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 |

```tsx
"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](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md).

## Identify signed-in users {#identify}

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.

```tsx title="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` {#snippet}

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`:

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

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

## Exports {#exports}

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

## Changelog {#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

- [Web widget and JS API](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md): every `HATANG_CONFIG` option, the `lead` command and conversion tracking.
- [Identity verification](https://hatang.dev/docs/guides/support/web-widget/identity-verification.md): signed identities and how sessions merge.
- [Install the widget](https://hatang.dev/docs/guides/support/web-widget/install.md): Shopify, Google Tag Manager, Webflow and plain HTML.

Source: https://hatang.dev/docs/guides/sdks/react
